Angular10 attribute directive mock is not working. Trying to get element in template: cannot read properties of null (reading 'nativeElement')

huangapple go评论92阅读模式
英文:

Angular10 attribute directive mock is not working. Trying to get element in template: cannot read properties of null (reading 'nativeElement')

问题

以下是您要翻译的内容:

"我正在尝试在我的Jasmine单元测试中模拟一个指令,使用的是Angular 10。该指令根据输入评估条件,将根据条件渲染或不渲染模板。使用模拟指令,我期望“讨厌的指令”将被忽略,并且能够获取原生元素。但是,似乎没有在模拟它。如果我从模板中删除包含讨厌的指令的div,并用注释掉的div替换,一切都正常工作。该指令使用一个存储库,但我不明白为什么我需要模拟它。谢谢!

要测试的组件的HTML模板:

<div *annoyingDirective="input_data1; input_data2: true" class="class1">
   <!--<div class="class1">-->
   <div class="class2">我正在尝试获取此元素
      <mat-slide-toggle
         [checked]="isChecked"
         class="test-toggle"
         (change)="toggleSlide($event)"
      ></mat-slide-toggle>
   </div>
</div>

单元测试:

@Directive({
   selector: '[annoyingDirective]'
})
export class MockAnnoyingDirective {
   @Input() input_data1: string[];
   @Input('input_data2') input_data2: boolean = false;
}

describe('ComponentToTest', () => {
   let component: ComponentToTest;
   let fixture: ComponentFixture<ComponentToTest>;

   beforeEach(async () => {
      TestBed.configureTestingModule({
         imports: [MatSlideToggleModule, TranslateModule.forRoot()],
         declarations: [ComponentToTest, MockAnnoyingDirective],
         providers: [..add providers]
      }).compileComponents();
   });
   beforeEach(() => {
      fixture = TestBed.createComponent(ComponentToTest);
      component = fixture.componentInstance;
      fixture.detectChanges();
   });

   it('应该创建组件', () => {
      expect(component).toBeTruthy(); //没有错误
   });

   describe('slide-toggle-selection', () => {
      it('应该显示div', () => {
         const forTest = fixture.debugElement.query(By.css(".class2")).nativeElement;
         expect(forTest).toBeTruthy(); //TypeError: Cannot read properties of null 
      });
   });
});

讨厌的指令:

@Directive({
   selector: '[annoyingDirective]'
})
export class AnnoyingDirective implements OnInit, OnDestroy {
   @Input() input_data1: string[];
   @Input('input_data2') strictAccess: boolean = false;

   //它使用一个存储库 - 我需要模拟它吗?
   @Select(UserState.permissions) permissions$: Observable<any[]>;

   constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef<any>) {}

   ngOnInit() {
      if (some condition based on permissions is met)
         return this.viewContainer.createEmbeddedView(this.templateRef);
   }
}
英文:

I'm trying to mock a Directive in my jasmine unit test using Angular 10. The directive evaluates a condition based on input and will either render or not render the template base on the condition. Using the mock directive, I'm expecting the "annoying directive" to be ignored, and to have the ability to grab native elements. However, it does not appear to be mocking it. If I remove the the div containing the annoyingDirective from the template and replace with the commented out div, everything works. The directive uses a store but I don't see why I would need to mock that. Thanks!

html template of Component to test

   &lt;div *annoyingDirective=&quot;input_data1; input_data2: true&quot; class=&quot;class1&quot;&gt;
      &lt;!--&lt;div class=&quot;class1&quot;&gt;--&gt;
      &lt;div class=&quot;class2&quot; &gt;I&#39;m trying to get this element
         &lt;mat-slide-toggle
          [checked]=&quot;isChecked&quot;
          class=&quot;test-toggle&quot;
          (change)=&quot;toggleSlide($event)&quot;
        &gt;
         &lt;/mat-slide-toggle&gt;
     &lt;/div&gt;
    &lt;/div&gt;

Unit test

    @Directive({
      selector: &#39;[annoyingDirective]&#39;
    })
    export class MockAnnoyingDirective {
      @Input() input_data1: string[];
      @Input(&#39;input_data2&#39;) input_data2: boolean = false;
    }

    describe(&#39;ComponentToTest&#39;, () =&gt; {
      let component: ComponentToTest;
      let fixture: ComponentFixture&lt;ComponentToTest&gt;;

      beforeEach(async () =&gt; {
        TestBed.configureTestingModule({
          imports: [MatSlideToggleModule, TranslateModule.forRoot()],
          declarations: [ComponentToTest, MockAnnoyingDirective],
          providers: [..add providers]
          ]
        }).compileComponents();
      });
      beforeEach(() =&gt; {
        fixture = TestBed.createComponent(ComponentToTest);
        component = fixture.componentInstance;
        fixture.detectChanges();
      });

      it(&#39;Should create component&#39;, () =&gt; {
        expect(component).toBeTruthy(); //no errors
       });

      describe(&#39;slide-toggle-selection&#39;, () =&gt; {
        it(&#39;should show the div&#39;, () =&gt; {
          const forTest = fixture.debugElement.query(By.css(&quot;.class2&quot;)).nativeElement;
          expect(forTest).toBeTruthy(); //TypeError: Cannot read properties of null 
      });
    });

annoying directive

    @Directive({
       selector: &#39;[annoyingDirective]&#39;
     })

    export class annoyingDirective implements OnInit, OnDestroy {
       @Input() input_data1: string[];
       @Input(&#39;input_data2&#39;) strictAccess: boolean = false;

       //it uses a store - do I need to mock this??
       @Select(UserState.permissions) permissions$: Observable&lt;any[]&gt;;

      constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef&lt;any&gt;) 
        {}
      ngOnInit(){
         if(some condition based on permissions is met)
           return this.viewContainer.createEmbeddedView(this.templateRef);
       }}

答案1

得分: 1

你可以尝试将你的MockAnnoyingDirective更改为以下内容:

@Directive({
  selector: '[annoyingDirective]'
})
export class MockAnnoyingDirective implements OnInit {
  @Input() input_data1: string[];
  @Input('input_data2') input_data2: boolean = false;

  constructor(
    private readonly templateRef: TemplateRef<any>,
    private readonly viewContainer: ViewContainerRef
  ) {}

  ngOnInit(): void {
    this.viewContainer.createEmbeddedView(this.templateRef);
  }
}

由于AnnoyingDirective是一个结构性指令(以*开头),在模拟中,我们必须绘制指令附加的内容(MockAnnoyingDirective中的ngOnInit),否则我们将没有div

英文:

Can you try changing your MockAnnoyingDirective to the following?

@Directive({
      selector: &#39;[annoyingDirective]&#39;
    })
    export class MockAnnoyingDirective implements OnInit {
      @Input() input_data1: string[];
      @Input(&#39;input_data2&#39;) input_data2: boolean = false;
      
      constructor(
        private readonly templateRef: TemplateRef&lt;any&gt;,
        private readonly viewContainer: ViewContainerRef
      ) {}

      ngOnInit(): void {
        this.viewContainer.createEmbeddedView(this.templateRef);
      }
    }

Since AnnoyingDirective is a structural directive (starts with *), in the mock, we have to paint what the directive is attached (the ngOnInit in the MockAnnoyingDirective) to or else we will have no div.

huangapple
  • 本文由 发表于 2023年8月5日 02:22:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76838354.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定