Jake Wilson
Jake Wilson

Reputation: 91243

How to assert in async function within Promise?

When using Mocha for unit testing, I have a situation like this that I need to test:

it('should assert true blah blah blah', function () {

  return doSomething() // <-- returns a Promise
    .then(function(value) {

      return setTimeout(function() {

        assert.equal(something, true);

      },1000);

    });

});

So I have a Promise that is returned from a function, and then I need to do an assert within an async setTimeout after the Promise has been resolved. The above code appears to always pass the test no matter what. In fact, it seems like the setTimeout never ever runs. If I put a console.log in the setTimeout it never prints out. Mocha finishes the unit test and moves on.

What is the proper way to test something like this? I see there is some sort of done() method with Mocha but I'm not sure if that is something I should use with this or not. I don't fully understand it.

Upvotes: 1

Views: 2174

Answers (3)

Mulan
Mulan

Reputation: 135425

Because Mocha will work intelligently with functions that return Promises, you could also use the new async/await syntax, which implicitly returns Promises!

import 'babel-polyfill';

it('should assert true blah blah blah', async function () {
  // `doSomething` is a function that returns a Promise
  // `something` will be the resolved value
  let something = await doSomething();
  assert.equal(something, true);
});

A little bit of setup is required, but I think it's worth it considering how nice and flat your testing code looks in the end. And it's super easy to write.

You'll probably want the following packages – es2015 and es2016 are optional here, but if you're transpiling your testing code, you might as well include these. You'll end up wanting them anyway. babel-register is used for the require hook and babel-polyfill is used for the regenerator runtime.

$ npm install --save-dev babel-cli           \
                         babel-register      \
                         babel-polyfill      \
                         babel-preset-es2015 \
                         babel-preset-es2016 \
                         babel-preset-es2017

Your .babelrc

{
  "presets": ["es2015", "es2016", "es2017"]
}

Your package.json

{
  "scripts": {
    "test": "mocha --compilers js:babel-register"
  }
}

Upvotes: 0

cartant
cartant

Reputation: 58440

If you return a promise to Mocha, it will assume the test is complete when the promise resolves.

If you have some non-promise-based asynchronous function to test after the promise resolves, you can use a done callback instead of returning a promise:

it('should assert true blah blah blah', function (done) {

  doSomething()
    .then(function(value) {

      setTimeout(function() {

        assert.equal(something, true);
        done();

      },1000);

    })
    .catch(done);
});

Note that you should also include a catch so that any promise rejection is passed to the done callback, as Mocha understands Node-style errors and will fail the test if it receives one.

Alternatively, you could use a completely promise-based approach and could create a Promise to enclose your asynchronous function:

it('should assert true blah blah blah', function () {

  return doSomething()
    .then(function(value) {

      return new Promise(function (resolve, reject) {

        setTimeout(function() {

          try {
            assert.equal(something, true);
            resolve();
          } catch (error) {
            reject(error);
          }

        },1000);

      });
    });
});

Upvotes: 1

notme
notme

Reputation: 444

You do not need to use setTimeout() to "wait" the Promise to be resolved. In stead, when it the Promise is resolved, the .then() will be call. You simply just use the value or do some assert there, that's it.

Upvotes: 0

Related Questions