Angular 测试中的 fakeAsync 和 async 有什么区别?

What is the difference between fakeAsync and async in Angular testing?

我知道 tick() 函数利用了 fakeAsync()。我也可以将 fixture.whenStable().then()async()fakeAsync() 一起使用。

我想知道它们的确切用例。谁能举例说明一下。

注意:我想在这两种情况下都使用 Fake Service 或 Stub。

tl;博士

在几乎所有情况下,它们都可以互换使用,但最好使用 fakeAsync()/tick() 组合,除非您需要进行 XHR 调用,在这种情况下您必须使用 async()/whenStable() 组合,因为 fakeAsync()不支持XHR调用。


在大多数情况下,它们可以互换使用。除了外部模板和样式未内联编译到用于测试的组件(即使用 SystemJS)。使用 SystemJS 时,XHR 调用是针对外部模板和样式进行的。 fakeAsync() 在进行 XHR 调用时不能使用。另一方面,当使用 Webpack 时,外部模板和样式被内联编译,所以你可以使用 fakeAsync().

除此之外,我认为这是风格偏好的问题。我可以说的一件事是想象您需要进行多个异步调用,例如 。您需要嵌套的 fixture.whenStable() 调用,当它们变得如此深时,它们看起来会变得非常难看。

someAsyncAction();
fixture.whenStable().then(() => {
  fixture.detectChanges();
  expect(something)

  changeSomethingElseAsynchronously();      
  fixture.whenStable().then(() => {
    fixture.detectChanges();
    expect(something);

    anotherAsyncAction();
    fixture.whenStable().then(() => {
      fixture.detectChanges()
      expect(somthingeElse)
    })
  })
})

如果没有所有那些 fixture.whenStable() 看起来 同步的代码,这可能看起来更清晰(并且更容易推理)。

tick();
fixture.detectChanges();
expect(something)

changeSomethingAsynchronously()
tick();
fixture.detectChanges();
expect(somethingElse)

changeSomethingAsynchronously()
tick();
fixture.detectChanges();
expect(somethingElse);

我可能要补充的另一件事是我的 OCD 部分 总是 需要检查我在 fixture.whenStable() 中的调用是否被调用

fixture.whenStable().then(() => {
  expect(...)
  console.log('called...')
})

假设您忘记将测试包装在 async 中。否则,测试将在 fixture.whenStable 决议之前完成,您永远不会知道。看起来测试通过了,这是误报。实际发生的是断言从未被调用过。

出于这个原因,我实际上已经远离 async。但是,如果您喜欢这种风格,并且相信自己总是将测试包装在 async 中,那么就坚持下去。但是对于 fakeAsync,一切都是同步调用的,所以断言不可能不被调用。

当我们有 HTTP 调用时使用 async 或 waitForAcync,当没有 HTTP 调用但可观察时使用 fakeAsync 或 promise 或 setTimeout(不使用 HTTP 调用。)

基本上,带有 tick 函数的 fakeAsync 会将时间提前指定的毫秒数,因此 tick(50000) 将执行任何在 50 秒内发生的异步任务,这些任务一目了然。因为它将时间提前了 50 秒。看下面的例子,setTimeout 在 async 或 waitForAcync 的情况下需要 50 秒来执行,但是 fakeAsynch 不需要时间。

describe('this test', () => {
  it('looks async but is synchronous', fakeAsync(() => {
       let flag = false;
       setTimeout(() => {
         flag = true;
       }, 50000);
       expect(flag).toBe(false);
       tick(25000);
       expect(flag).toBe(false);
       tick(25000);
       expect(flag).toBe(true);
     }));
});