What is the difference between fakeAsync's tick() and done() in angular2 testing?

11,307

Those 2 things have nothing in common.

done is just a callback to let your test runner know when an async operation is done.

For example:

it('should wait for this promise to finish', done => {
  const p = new Promise((resolve, reject) =>
    setTimeout(() => resolve(`I'm the promise result`), 1000)
  );

  p.then(result => {
    // following will display "I'm the promise result" after 1s
    console.log(result);

    // this let your test runner know that it can move forward
    // because we're done here
    // the test will take 1s due to the setTimeout at 1000ms
    done();
  });
});

You might also use async for that (just to avoid calling done manually):

it(
  'should wait for this promise to finish',
  async(() => {
    const p = new Promise((resolve, reject) =>
      setTimeout(() => resolve(`I'm the promise result`), 1000)
    );

    p.then(result =>
      // following will display "I'm the promise result" after 1s
      console.log(result)
    );

    // notice that we didn't call `done` here thanks to async
    // which created a special zone from zone.js
    // this test is now aware of pending async operation and will wait
    // for it before passing to the next one
  })
);

Now, fakeAsync gives you control over time (which is really powerful) so you can write your tests in a synchronous way, and simulate that time goes by to avoid waiting for setTimeout for example:

it(
  'should wait for this promise to finish',
  fakeAsync(() => {
    const p = new Promise((resolve, reject) =>
      setTimeout(() => resolve(`I'm the promise result`), 1000)
    );

    // simulates time moving forward and executing async tasks
    flush();

    p.then(result =>
      // following will display "I'm the promise result" **instantly**
      console.log(result)
    );

    // notice that we didn't call `done` here has there's no async task pending
  })
);

So just to be clear, with fakeAsync in the last example, if the setTimeout was set on 10s, the test would still be executed instantly.

Share:
11,307
Sanju
Author by

Sanju

A dab of programming, A spoonful of curiosity and a tumbler full of coffee make me, me.

Updated on July 28, 2022

Comments

  • Sanju
    Sanju almost 2 years

    I'm trying to figure out what differentiates fakeAsync's tick() method from done() as suggested by some answers on stack overflow.

    Using tick() we can simulate a timeout, but can we accomplish the same using done()?

    Why does angular consider it more viable method than using async or fakeAsync?

    Take for example.

    This method works for me...

    it("Should display names",(done:any) => {
            component.names = [
                {
                    "firstname": "abc",
                    "lastname": "max"
                },
                {
                    "firstname": "def",
                    "lastname": "max"
                },
            ];
            done();
            fixture.detectChanges();
            let wrapBox = fixture.debugElement.queryAll(By.css('.wrapBox'));
            console.log(wrapBox);
    });
    

    But following method returns '6 timer(s) still in queue' error...

    it("Should display names",fakeAsync(() => {
            component.names = [
                {
                    "firstname": "abc",
                    "lastname": "max"
                },
                {
                    "firstname": "def",
                    "lastname": "max"
                },
            ];
            tick();
            fixture.detectChanges();
            let wrapBox = fixture.debugElement.queryAll(By.css('.wrapBox'));
            console.log(wrapBox);
    }));
    

    Note:

    1. The data for array names is asynchronous since it's retrieved from back end using 'get' operation. But here i'm mocking the data.

    2. The data in array is iterated through and passed to another child component which displays it in the view.

  • Max Koretskyi
    Max Koretskyi over 6 years
    also you can add that done is a native Jasmine method while async and fakeAsync are added by Angular testing framework
  • maxime1992
    maxime1992 over 6 years
    Indeed, you'll only be able to use async and fakeAsync by importing them from angular/core/testing :)
  • Sanju
    Sanju over 6 years
    @Maxime Great explanation! Thanks!
  • Sanju
    Sanju over 6 years
    @Maxime Can you give an instance, when each of these scenarios are preferred over others?
  • maxime1992
    maxime1992 over 6 years
    I mostly use async over done callbacks. But I've read that it might be slowering tests down so not sure it's a good idea. About fakeAsync, it's really handy to avoid waiting and taking back control over time, that's just it :)
  • Sanju
    Sanju over 6 years
    @Maxime Thanks!
  • EugenSunic
    EugenSunic about 6 years
    Where is tick() mentioned in all of this story?
  • maxime1992
    maxime1992 about 6 years
    Tick is nearly the same as flush. Flush will run all the asynchronous tasks in queue and tick will too if no arguments are provided. But you can also decide to pass a time in ms, and it'll run the tasks that are only supposed to be run during that time. So for example if you have to setTimeout, one at 2s and the other at 1, run tick(1000) and only the first callback will have been called.