【问题标题】:Unit testing angular 5 component with @ViewChild使用 @ViewChild 对 Angular 5 组件进行单元测试
【发布时间】:2019-01-02 01:46:51
【问题描述】:

我正在使用 Angular 5.2.0。我有一个子组件

import { Component } from '@angular/core';

@Component({
    template: `<div><\div>`
})
export class ChildComponent {

    public childMethod() {
        ...
    }
}

以及一个通过ViewChild访问子组件的父组件

import { Component, ViewChild } from '@angular/core';
import { ChildComponent } from 'child.component';

@Component({
    template: `<child-component #child><\child-component>`
})
export class ParentComponent {

    @ViewChild('child')
    public child: ChildComponent;

    public parentMethod() {
        this.child.childMethod();
    }
}

我想要一个单元测试来证明parentMethod 的调用会导致childMethod 的调用。我有以下内容:

import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ChildComponent } from './child.component';
import { ParentComponent } from './parent.component';

describe('ParentComponent', () => {

    let component: Parentcomponent;
    let fixture: ComponentFixture<Parentcomponent>;

    beforeEach(() => {
        TestBed.configureTestingModule({
            declarations: [ ParentComponent, ChildComponent ],
            schemas: [ NO_ERRORS_SCHEMA ]
    }).compileComponents();
});

beforeEach(() => {
    fixture = TestBed.createComponent(TaskListPaginatorComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
});

    it('should invoke childMethod when parentMethod is invoked', () => {
        const childMethodSpy: jasmine.Spy = spyOn(component.child, 'childMethod');
        component.parentMethod();
        expect(childMethodSpy).toHaveBeenCalled();
    });

});

然而,这不起作用,我得到Error: &lt;spyOn&gt; : could not find an object to spy upon for childMethod()。

此外,这不是单元测试,因为我使用的是真正的 ChildComponent 而不是模拟。我尝试创建一个 MockChildComponent 并将其添加到 declarations 和 export 但我得到了相同的结果。有什么帮助吗?

我知道有类似的帖子,但它们适用于不同版本的 Angular,它们没有帮助。

【问题讨论】:

  • 您应该避免使用 NO_ERRORS_SCHEMA。如果您删除它,它会告诉您示例中缺少选择器。使用选择器,您的示例有效。见stackblitz.com/edit/angular-testing-ta7mfv 当然你是对的,嘲笑ChildComponent 是更好的解决方案。

标签: angular unit-testing viewchild testbed


【解决方案1】:

另外,覆盖此类代码的快速简便方法....

如果 .ts 文件是这样的:

@ViewChild('datePicker') myDatePicker: AngularMyDatePickerDirective

然后在 it() 块内的 spec.ts 文件中,您可以添加如下内容:

component.myDatePicker = { toggleCalender: () => Promise.resolve()} as unknown as AngularMyDatePickerDirective;

【讨论】:

    【解决方案2】:

    可以mock ChildComponent,因此可以单独测试ParentComponent,不需要手动分配ChildComponent。

    如果 ParentComponent.childComponent 是私有的,这是必需的,如果 ChildComponent 有很多依赖项,则几乎是必需的。

    您的测试文件应如下所示:

    import { ComponentFixture, TestBed } from '@angular/core/testing';
    import { ChildComponent } from './child.component';
    import { ParentComponent } from './parent.component';
    
    @Component({
      selector: 'child-component',
      template: '',
      providers: [{ provide: ChildComponent, useClass: ChildStubComponent }]
    })
    class ChildStubComponent {
      childMethod = jasmine.createSpy('childMethod');
    }
    
    describe('ParentComponent', () => {S
      let component: ParentComponent;
      let fixture: ComponentFixture<ParentComponent>;
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          declarations: [ ParentComponent, ChildStubComponent ]
        }).compileComponents();
      });
    
      beforeEach(() => {
        fixture = TestBed.createComponent(ParentComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
      });
    
      it('should invoke childMethod when parentMethod is invoked', () => {
        component.childComponent =  childComponent;
        component.parentMethod();
        expect(childComponent.childMethod).toHaveBeenCalled();
      });
    });
    

    学分: 来自Angular inDepth 的想法。 基于@Anuradha Gunasekara 的回答。

    【讨论】:

      【解决方案3】:

      你可以这样做。

      像这样为ChildComponent 创建一个间谍对象。

      const childComponent = jasmine.createSpyObj('ChildComponent', ['childMethod']);
      

      然后在测试中,将组件的 childComponent 属性设置为你创建的 spy。

        component.childComponent =  childComponent;
      

      您的测试文件应如下所示。

      import { NO_ERRORS_SCHEMA } from '@angular/core';
      import { ComponentFixture, TestBed } from '@angular/core/testing';
      import { ChildComponent } from './child.component';
      import { ParentComponent } from './parent.component';
      
      describe('ParentComponent', () => {
      
          let component: ParentComponent;
          let fixture: ComponentFixture<ParentComponent>;
      
          const childComponent = jasmine.createSpyObj('ChildComponent', ['childMethod']);
      
          beforeEach(() => {
              TestBed.configureTestingModule({
                  declarations: [ ParentComponent, ChildComponent ],
                  schemas: [ NO_ERRORS_SCHEMA ]
          }).compileComponents();
      });
      
      beforeEach(() => {
          fixture = TestBed.createComponent(ParentComponent);
          component = fixture.componentInstance;
          fixture.detectChanges();
      });
      
          it('should invoke childMethod when parentMethod is invoked', () => {
              component.childComponent =  childComponent;
              component.parentMethod();
              expect(childComponent.childMethod).toHaveBeenCalled();
          });
      
      });
      

      【讨论】:

      • 感谢伙计,这非常有效!我没有考虑使用 createSpyObj,这是关键!
      • 如果此答案真的对您有所帮助,请将其标记为正确答案。谢谢老哥
      • 实际上,根据您的建议,我什至不必导入 ChildComponent 并将其添加到声明中。再次感谢
      • 是的,请记住单元测试是测试单个单元。所以你不必测试或声明真正的子组件。由于您没有测试模板,因此您可以为子组件创建一个 spy。
      • 你对@viewChildren 的看法如何? @Anuradha Gunasekara
      猜你喜欢
      • 1970-01-01
      • 2018-12-14
      • 2021-03-11
      • 2016-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多