Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assert Is Breaking Async Function in Mocha Test

I'm building a node module and am trying to do my best to unit test the heck out of it. I've setup mocha and chai to do the test handling. I'm having a problem testing my async methods (methods that return promises).

In the following test I am testing a method on an "Upgrade" object.

  it('Should return a list of versions for the default git repo', function (done) {
    fs.writeFileSync(appSetup.CONFIG_FILENAME, JSON.stringify(appSetup.DEFAULT_CONFIG));

    var upgrade = new Upgrade({
      quiet: true
    });

    upgrade.getVersions().then(function (versions) {
      assert(versions && versions.length > 0, 'Should have at least one version.');
      assert.equal(1, 2); // this throws the exception which causes the test case not even exist
      done();
    }, done);
  });

The getVersions() call returns a promise as the method is async. When the promise resolves I want to test the value returned in the versions variable.

The assert(versions && versions.length > 0, 'Should have at least one version.'); is the actual test. I added assert.equal(1, 2); because I noticed that when the test should fail the test case wouldn't even show up in the test list.

I'm assuming that the assert call is throwing an exception that Mocha should pickup. However it is getting trapped in the promises then handler function.

What is going on here? Why when the assert is going to fail in that method does it not show the test case in the list (it doesn't show as failed; like it doesn't exist)?

like image 971
Mike G Avatar asked Jan 27 '15 22:01

Mike G


2 Answers

The core of the issue is that the code you have is essentially:

try {
  var versions = upgrade.getVersions();
} catch (err){
  return done(err);
}

assert(versions && versions.length > 0, 'Should have at least one version.');
assert.equal(1, 2); // this throws the exception which causes the test case not even exist
done();

Looking at that, it should be clear that if the assertions throw, then neither callback will run.

try {
  var versions = upgrade.getVersions();
  assert(versions && versions.length > 0, 'Should have at least one version.');
  assert.equal(1, 2); // this throws the exception which causes the test case not even exist
  done();
} catch (err){
  return done(err);
}

is more like what you want, which would be:

upgrade.getVersions().then(function (versions) {
  assert(versions && versions.length > 0, 'Should have at least one version.');
  assert.equal(1, 2); // this throws the exception which causes the test case not even exist
}).then(done, done);

Node that this will execute the assertions and then move the callbacks into a secondary .then() that will always handle the errors.

That said, it would be much easier to simply return the promise as

return upgrade.getVersions().then(function (versions) {
  assert(versions && versions.length > 0, 'Should have at least one version.');
  assert.equal(1, 2); // this throws the exception which causes the test case not even exist
});

to let Mocha monitor the promise itself without the callback.

like image 110
loganfsmyth Avatar answered Oct 13 '22 03:10

loganfsmyth


The test doesn't show up in the list until you call the callback, which never happens if that assert fails. You would need to call .catch(done) on the final promise to ensure done is always called.

The test will show up if you give it a timeout value, which you should probably do.

All that said, mocha understands promises. You don't need to deal with callbacks at all:

  it('Should return a list of versions for the default git repo', function () {
    fs.writeFileSync(appSetup.CONFIG_FILENAME, JSON.stringify(appSetup.DEFAULT_CONFIG));

    var upgrade = new Upgrade({
      quiet: true
    });

    return upgrade.getVersions().then(function (versions) {
      assert(versions && versions.length > 0, 'Should have at least one version.');
      assert.equal(1, 2);
    });
  });
like image 33
Aaron Dufour Avatar answered Oct 13 '22 03:10

Aaron Dufour