angular - 如何检查测试期间调用内部组件的方法?

标签 angular unit-testing testing jasmine ng-mocks

我有一个通过 HexesService 调用外部 API 的组件 (MapComponent)。此 API 调用的数据结果将加载到一些按钮(查看 map 、编辑 map )。我有另一个内部组件 (HexesComponent),单击“ View ”按钮会调用此内部组件(这又会进行另一个 API 调用并显示 HexesComponent 内部的数据)。

我已经使用外部服务的模拟创建了一个测试。我的测试检查主组件加载后是否调用模拟服务并正确加载数据(所有按钮均已填充)。这工作得很好。

接下来我要检查的是,单击按钮是否会调用 HexesComponent 的方法。

我遇到的问题是我的内部组件总是未定义。 我已经为我的内部组件创建了一个 stub ,但这没有帮助:甚至 stub 也是空的。

问题#1: 我做错了什么?我尝试过对“beforeEach”内的方法使用异步,但这没有帮助。

不用说,除了“测试”之外,该功能运行得非常好。

问题#2: 如何验证单击按钮是否会调用 HexComponent 的“loadMap”方法?

这是我的测试代码:

class MockHexesService extends HexesService {
  getDataForButtons(){
    return of(...);
  }
  ...
}

@Component({ selector: 'app-hex', template: '' })
class HexStubComponent {
   public loadMap = jasmine.createSpy('loadMap');
}

describe('MapsComponent', () => {
  let component: MapsComponent;
  let hexComponent: HexStubComponent;
  let fixture: ComponentFixture<MapsComponent>;
  let componentService: HexesService;
  let hexComponentSpy: any;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientModule],
      declarations: [MapsComponent, HexStubComponent],
      providers: [HexesService],
    }).compileComponents();

    TestBed.overrideComponent(MapsComponent, {
      set: {
        providers: [{ provide: HexesService, useClass: MockHexesService }],
      },
    });

    fixture = TestBed.createComponent(MapsComponent);
    component = fixture.componentInstance;
    componentService = fixture.debugElement.injector.get(HexesService);

    fixture.detectChanges();

    hexComponent = fixture.debugElement.injector.get(HexStubComponent);
    hexComponentSpy = spyOn(hexComponent, 'loadMap');
  });

  it('Loaded map descriptions should load data for buttons', () => {
    const tblRows =
      fixture.debugElement.nativeElement.querySelectorAll('table tr');
    const cells0 = tblRows[0].querySelectorAll('td');
    expect(cells0[0].innerHTML).toBe('Map 1');
    ... checking other buttons are populated properly

    expect(component.hexComponent).toBeTruthy(); // - this fails here
  });

  it('Click on "View" button should load the map', async(() => {
    //spyOn(component.hexComponent.loadMap)
    const btns = fixture.debugElement.nativeElement.querySelectorAll('button');
    expect(btns.length).toBe(6);
    const btnView = btns[0];
    expect(btnView.innerHTML).toBe('View Map');
    btnView.click();

    fixture.whenStable().then(() => {
      expect(component.hexComponent).toBeTruthy(); // - this fails 
      // and how do I check that 'loadMap' method of hexComponent is called with proper data?
    });
  }));
});

maps.component.html 的基本部分:

...
<tr *ngFor="let d1 of data">
  ...    
  <button (click)="loadMap(d1.mapId)">View Map</button>
  ...
</tr>
<app-hex id="hexComponent"></app-hex>

maps.component.ts 的基本部分:

export class MapsComponent implements OnInit {
  @ViewChild(HexComponent) hexComponent: HexComponent;

  constructor(public ngZone: NgZone, private hexesService: HexesService) {}

  ngOnInit(): void {
    this.hexesService
      .getDataForButtons()
      .subscribe((data: any) => this.populateButtons(data));
  }

  loadMap(mapId): void {
    this.hexComponent.loadMap(mapId);
  }

附注我发现了一个类似的问题( How can I use a fake/mock/stub child component when testing a component that has ViewChildren in Angular 10+? ),建议使用 ng-mocks,但无法使它们工作。

P.P.S。我尝试过使用

@Component({ selector: 'app-hex', template: '' })
class HexStubComponent {
  //public loadMap(mapId: number) {}
  public loadMap = jasmine.createSpy('loadMap');
}

并在“TestBed.configureTestingModule”的声明中使用 HexStubComponent,但我的测试给出错误:

NullInjectorError: R3InjectorError(DynamicTestModule)[HexStubComponent -> HexStubComponent]: NullInjectorError: No provider for HexStubComponent!

不知道如何解决这个问题。

最佳答案

我决定再次尝试并使其与 ng-mocks 一起使用,而不是插入使用 Stabs 的选项。

显然,我的问题接近 Mocking Child Components - Angular 2 好的指导是 https://www.npmjs.com/package/ng-mocks/v/11.1.4 不同之处在于我的情况,我还需要确保调用子组件的一些方法。

为了帮助其他人(也许将来也能帮助我自己),这里列出了我在路上遇到的问题。

  • 使用“npm install ng-mocks --save-dev”命令安装 ng-mocks

  • 添加导入行(很明显,但 VS Code 没有自动建议这些行,我很难意识到如何访问我需要的确切类):

    import { MockComponent, MockInstance, ngMocks } from 'ng-mocks';
    
  • 在 TestBed.configureTestingModule 的异步版本中添加组件的模拟:

    declarations: [MapsComponent, MockComponent(HexComponent)],
    
  • 在您需要检查的组件和方法上创建 spy :

    beforeEach(() => MockInstance(HexComponent, 'loadMap', jasmine.createSpy()));
    
  • 验证:

    it('Click on "View" button should load the map', async(() => {
      const hexComponent = ngMocks.findInstance(HexComponent);
      expect(hexComponent).toBeDefined();
      expect(hexComponent.loadMap).not.toHaveBeenCalled();
    
      const btns = fixture.debugElement.nativeElement.querySelectorAll('button');
      expect(btns.length).toBe(6);
      const btnView = btns[0];
      expect(btnView.innerHTML).toBe('View Map');
    
      btnView.click();
    
      fixture.whenStable().then(() => {
        expect(hexComponent.loadMap).toHaveBeenCalled();
      });
    }));
    

我尚未解决的唯一问题是模拟 HexComponent 使用的某些服务。我稍后会尝试一下。

关于angular - 如何检查测试期间调用内部组件的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68325705/

相关文章:

Angular 2 - 变更检测问题

Angular Firestore subscribe() 触发两次

c# - 在构建服务器 : Release or Debug code? 上进行单元测试

javascript - Angular 2 - Kendo UI 下拉列表默认值

javascript - 如何使用 GULP/NODE 在 JavaScript 中获取 Jenkins BUILD_NUMBER?

c# - 模拟 protected 方法

android - 在AndroidStudio 3.1.1上更新Gradle后找不到AndroidJUnit4

Django - setUpTestData 和多对多关系

javascript - Protractor - 检查一个元素是否在另一个元素旁边

ruby-on-rails - HTTP URL 可用性测试