角度测试:将fakeAsync与async / await一起使用

时间:2020-05-11 21:22:05

标签: angular unit-testing angular-material

Angular Material提供component harnesses进行测试,通过await兑现承诺,您可以与它们的组件进行交互,如下所示:

  it('should click button', async () => {
    const matButton = await loader.getHarness(MatButtonHarness);
    await matButton.click();
    expect(...);
  });

但是,如果单击按钮会触发延迟操作怎么办?通常,我会使用fakeAsync() / tick()来处理它:

  it('should click button', fakeAsync(() => {
    mockService.load.and.returnValue(of(mockResults).pipe(delay(1000)));
    // click button
    tick(1000);
    fixture.detectChanges();
    expect(...);
  }));

但是我有什么办法可以在同一测试中做到这两者?

async内包装fakeAsync()函数会给我“错误:代码应该在fakeAsync区域中运行才能调用此函数”,大概是因为一旦完成await,它就是不再与我传递给fakeAsync()的函数相同。

我是否需要做这样的事情-等待之后启动一个fakeAsync函数?还是有一种更优雅的方式?

  it('should click button', async () => {
    mockService.load.and.returnValue(of(mockResults).pipe(delay(1000)));
    const matButton = await loader.getHarness(MatButtonHarness);

    fakeAsync(async () => {
      // not awaiting click here, so I can tick() first
      const click = matButton.click(); 
      tick(1000);
      fixture.detectChanges();
      await click;
      expect(...);
    })();
  });

2 个答案:

答案 0 :(得分:2)

async内不需要fakeAsync(真实),至少可以控制模拟的时间流。 fakeAsync的重点是允许您将await替换为tick / flush。现在,当您真正需要该值时,我认为您陷入了then的困境,就像这样:

  it('should click button', fakeAsync(() => {
    mockService.load.and.returnValue(of(mockResults).pipe(delay(1000)));
    const resultThing = fixture.debugElement.query(By.css("div.result"));
    loader.getHarness(MatButtonHarness).then(matButton => {
      matButton.click(); 
      expect(resultThing.textContent).toBeFalsy(); // `Service#load` observable has not emitted yet
      tick(1000); // cause observable to emit
      expect(resultThing.textContent).toBe(mockResults); // Expect element content to be updated
    });
  }));

现在,由于您的测试主体函数位于对fakeAsync的调用中,因此它应该1)在解决所有创建的Promises(包括getHarness返回的Promises)之前,不允许测试完成,和2)如果有任何待处理任务,则测试失败。

(顺便说一句,如果您将fixture.detectChanges()管道与服务返回的Observable一起使用,我认为在第二个expect之前不需要async,因为async管道在内部订阅触发时会显式地调用所有者的更改检测器。不过,我很想知道我是否错了。

答案 1 :(得分:1)

我刚刚发布了一个测试助手,可让您完全按照自己的要求进行操作。除其他功能外,它还允许您在 fakeAsync 测试中使用材料线束,并按照您的描述控制时间的流逝。

帮助程序会自动运行您在伪异步区域中传递给其 .run() 方法的内容,并且它可以处理 async/await。它看起来像这样,您可以在其中创建 ctx 助手来代替 TestBed.createComponent()(无论您在哪里这样做):

it('should click button', () => {
  mockService.load.and.returnValue(of(mockResults).pipe(delay(1000)));
  ctx.run(async () => {
    const matButton = await ctx.getHarness(MatButtonHarness);
    await matButton.click();
    ctx.tick(1000);
    expect(...);
  });
});

该库名为 @s-libs/ng-dev。查看此特定助手 here 的文档,并通过 github here 让我知道任何问题。

相关问题