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

Posted

技术标签:

【中文标题】Angular 测试中的 fakeAsync 和 async 有啥区别?【英文标题】:What is the difference between fakeAsync and async in Angular testing?Angular 测试中的 fakeAsync 和 async 有什么区别? 【发布时间】:2017-08-15 17:47:27 【问题描述】:

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

我想知道它们的确切用例。谁能用例子解释一下。

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

【问题讨论】:

【参考方案1】:

tl;博士

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


在大多数情况下,它们可以互换使用。除了外部模板和样式未内联编译到组件中以进行测试的组件的情况外,我想不出任何一个必需的组件(即使用 SystemJS)。使用 SystemJS 时,会对外部模板和样式进行 XHR 调用。 fakeAsync() 在有 XHR 调用时不能使用。另一方面,在使用 Webpack 时,外部模板和样式会被内联编译,因此您可以使用 fakeAsync()

除此之外,我认为这是风格偏好的问题。我可以说的一件事是假设您需要进行多个异步调用,例如在this example 中。您需要嵌套的 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()s 和看起来同步的代码,这可能看起来更简洁(并且更容易推理)。

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,一切都是同步调用的,所以断言不可能不被调用。

【讨论】:

我同意您提出的编码问题。所以你更喜欢使用 fakeAsync 而不是 async,但根据文档 fakeAsync 仍然是实验性的。我通过异步使用它是否安全? 这样说吧。表单模块中的所有测试用于角度使用fakeAsync。我还没有真正查看其他模块的源代码,但我敢打赌其中一些也使用它 也许我错了,但不应该在 fixture.detectChanges() 之前调用 tick(); 吗? @M'sieurToph' 这只是伪代码。我没有想到任何具体的例子。但你是对的,应该是这样的。 @PaulSamsotha 关于Imagine that you forgot to wrap the test in async...the assertion was never even called.,在我看来,如果曾经是这种情况,现在可能实现已经改变,因为我可以从it 中删除async 关键字当在fixture.whenStable().then(..) 中编写时,回调和断言将通过/失败测试我知道断言正在执行,因为我可以使测试通过或失败。从当前的角度文档; The fixture.whenStable() returns a promise that resolves when the **javascript engine's task queue** becomes empty..【参考方案2】:

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

带有tick函数的fakeAsync基本上会将时间提前指定的毫秒数,因此tick(50000)将执行任何将在50秒内发生的异步任务,一眼就能完成。因为它将时间提前了 50 秒。看下面的例子,在 async 或 waitForAcync 的情况下 setTimeout 需要 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);
     ));
);

【讨论】:

以上是关于Angular 测试中的 fakeAsync 和 async 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2 fakeAsync 在使用tick()的函数中等待超时?

角度测试中的 tick() 和 flush() 有啥区别?

为使用 Observables 的 Angular 2 组件编写 Jasmine 测试

Angular2 NgModel 在 Jasmine 测试中没有获得价值

Angular 混合应用程序测试“无法读取 null 的属性‘nativeElement’”

Angular2 测试:ComponentFixture 中的 DebugElement 和 NativeElement 对象有啥区别?