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

2

37

I know that tick() function utilizes fakeAsync(). And also I can use fixture.whenStable().then() with async() and fakeAsync() as well.

I want to know the exact use case for both of them. Can anyone explain this with examples.

Note : I want to use Fake Service or Stub in both the scenarios.

Berck answered 23/3, 2017 at 9:9 Comment(1)
I wonder the odds of the editor and the author having the same icon?Heighten
B
46

tl;dr

In almost all cases, they can be used interchangeably, but using fakeAsync()/tick() combo is preferred unless you need to make an XHR call, in which case you MUST use async()/whenStable() combo, as fakeAsync() does not support XHR calls.


For the most part they can be used interchangeably. I can't think of anything off the top of my head in which one is required over the other, except for the case of components whose external templates and styles are not compiled inline in to the component for testing (i.e. using SystemJS). When using SystemJS, XHR calls are made for the external templates and styles. fakeAsync() cannot be used when there are XHR calls made. On the other hand, when using Webpack, the external templates and styles get compiled inline, so you can use fakeAsync().

Other than that, I think it's a matter of style preference. One thing I can say is imagine you need to make multiple calls that are asynchronous, like in this example. You need nested fixture.whenStable() calls, which can start to look to pretty ugly when they get so deep.

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

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

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

This might look cleaner (and easier to reason about) without all those fixture.whenStable()s and code that looks synchronous.

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

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

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

Another thing I might add is the OCD part of me always needs to check that my calls in the fixture.whenStable() is called

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

Imagine that you forgot to wrap the test in async. Without that, the test will complete before the fixture.whenStable resolution, and you will never know it. It will look like the test passed, which is a false positive. What actually happened is that the assertion was never even called.

For this reason, I've actually been moving away from async. But if you like that style, and trust yourself that you always wrap the test in async, then stick with it. But with fakeAsync, everything is called synchronously, so there's no chance of the assertion not being called.

Brownedoff answered 23/3, 2017 at 9:47 Comment(5)
I agree the coding concerns that you made. So you prefer to use fakeAsync over async, but according to the documentation fakeAsync is still experimental. Is it safe for me to use that over async?Berck
Put it this way. All the tests in the forms module for angular use fakeAsync. I havent really looked at the source for other modules, but I bet some of them use it alsoBrownedoff
Maybe I am wrong, but should not tick(); be called before the fixture.detectChanges() ?Instigate
@M'sieurToph' it's just pseudo-code. I didn't have any specific example in mind. But you're right, it probably should be that way.Brownedoff
@PaulSamsotha In regard to Imagine that you forgot to wrap the test in async...the assertion was never even called., it looks to me like, if this was once the case that maybe the implementation has now changed because I can remove the async keyword from the it callback and assertions will pass/fail the test when written inside fixture.whenStable().then(..) I know the assertions are executing because I can make the test pass or fail. From the current angular docs; The fixture.whenStable() returns a promise that resolves when the **JavaScript engine's task queue** becomes empty..Lama
R
4

Use async or waitForAcync when we have HTTP call and use fakeAsync when there is no HTTP call but observable Or promise or setTimeout (which do not use HTTP calls.)

Basically fakeAsync with tick function will advance time by a specified number of milliseconds, so tick(50000) would execute any asynchronous tasks that would occur in 50 seconds will be completed in the glance of the eye. because it advances time by 50 seconds. look at the below example as setTimeout needs 50 seconds to execute in case of async or waitForAcync, but fakeAsynch will not take time.

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);
     }));
});
Readership answered 4/5, 2021 at 13:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.