Categories
Testing

Jasmine — Async Tests

Spread the love

Testing is an important part of JavaScript.

In this article, we’ll look at how to create more complex tests with Jasmine.

Testing Promises

We can test promises with Jasmine.

The beforeAll , afterAll , beforeEach and afterEach functions all support promises.

For instance, we can write:

describe("Using promises", function () {
  let value;

  beforeEach(function () {
    return Promise.resolve().then(function () {
      value = 0;
    });
  });

  it("should support async execution of tests", function () {
    return Promise.resolve().then(function () {
      value++;
      expect(value).toBeGreaterThan(0);
    });
  });
});

We called then on a promise in the beforeEach and it callbacks.

Then we can check the values in there.

It also works with the async and await syntax.

For instance, we can write:

describe("Using promises", function () {
  let value;

  beforeEach(async function () {
    await Promise.resolve()
    value = 0;
  });

  it("should support async execution of tests", async function () {
    await Promise.resolve()
    value++;
    expect(value).toBeGreaterThan(0);
  });
});

We just rewrote the promises with the async and await syntax.

In both examples, they’ll run sequentially.

The test starts with the beforeEach callback and then run the it callback.

Changing Wait Time

The beforeEach and afterEach functions take an optional 2nd argument to set the wait until done is finished.

For example, we can write:

describe("long asynchronous specs", function () {
  beforeEach(function (done) {
    done();
  }, 1000);

  it("takes a long time", function (done) {
    setTimeout(function () {
      done();
    }, 9000);
  }, 10000);

  afterEach(function (done) {
    done();
  }, 1000);
});

to pass in a 2nd argument with the wait time in milliseconds.

Handling Failures

Handling failures with promises can be done like any other piece of code.

If a promise is rejected in our test, then the test will fail.

For instance, if we have:

describe("promise test", function () {
  it('does a thing', function () {
    return Promise.reject()
  });
});

then the test will fail.

With async and await , we get the same result:

describe("long asynchronous specs", function () {
  it('does a thing', async function () {
    await Promise.reject()
  });
});

This will also fail.

We can fail a test with callbacks.

Since Jasmine 3.0, the done function can detect if an Error object is passed to the function.

If it is, then the test will fail.

For instance, we can write:

describe("long asynchronous specs", function () {
  beforeEach(function (done) {
    setTimeout(function () {
      let err;

      try {
        throw new Error()
      } catch (e) {
        err = e;
      }

      done(err);
    });
  });

  it('does something', () => { })
});

Our beforeEach callback throw an error.

So since we call done with the err object, we’ll see that the test will fail.

Mock Clock

We can use the mock clock to avoid writing async tests.

It’ll make async code run synchronously.

For instance, we can write:

describe("long asynchronous specs", function () {
  beforeEach(function () {
    jasmine.clock().install();
  });

  afterEach(function () {
    jasmine.clock().uninstall();
  });

  it('does something after 10 seconds', function () {
    const callback = jasmine.createSpy('callback');
    setTimeout(function () {
      callback('foo');
    }, 10000);
    jasmine.clock().tick(10000);
    expect(callback).toHaveBeenCalledWith('foo');
  });
});

to create a test with the mock clock.

We called jasmine.clock().install() to create the clock.

And then we run the test code by calling tick to move to the time we want.

Then when the test is done, we call jasmine.clock().uninstall() to remove the mock clock.

Conclusion

We can test various kinds of async code with Jasmine.

Promises and callbacks are all supported.

And we can make timer code synchronous with a mock clock.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *