简体   繁体   中英

How does Mocha know about test failure in an asynchronous test?

I am trying to understand how the asynchronous code for Mocha (at http://mochajs.org/#getting-started ) works.

describe('User', function() {
  describe('#save()', function() {
    it('should save without error', function(done) {
      var user = new User('Luna');
      user.save(function(err) {
        if (err) throw err;
        done();
      });
    });
  });
});

I want to know how Mocha decides whether a test has succeeded or failed behind the scenes.

I can understand from the above code that user.save() being asynchronous would return immediately. So Mocha would not decide if the test has succeeded or failed after it executes it() . When user.save() ends up calling done() successfully, that's when Mocha would consider it to be a successful test.

I cannot understand how it Mocha would ever come to know about a test failure in the above case. Say, user.save() calls its callback with the err argument set, then the callback throws an error. None of Mocha's function was called in this case. Then how would Mocha know that an error occurred in the callback?

Mocha is able to detect failures that prevent calling the callback or returning a promise because it uses process.on('uncaughtException', ...); to detect exceptions which are not caught. Since it runs all tests serially, it always knows to which test an uncaught exception belongs. (Sometimes people are confused by this: telling Mocha a test is asynchronous does not mean Mocha will run it in parallel with other tests. It just tells Mocha it should wait for a callback or a promise.)

Unless there is something that intervenes to swallow exceptions, Mocha will know that the test failed and will report the error as soon as it detects it. Here is an illustration. The first test fails due to a generic exception thrown. The 2nd one fails due to an expect check that failed. It also raises an unhandled exception.

var chai = require("chai");
var expect = chai.expect;

it("failing test", function (done) {
    setTimeout(function () {
        throw new Error("pow!");
        done();
    }, 1000);
});

it("failing expect", function (done) {
    setTimeout(function () {
        expect(1).to.equal(2);
        done();
    }, 1000);
});

This is the output on my console:

  1) failing test
  2) failing expect

  0 passing (2s)
  2 failing

  1)  failing test:
     Uncaught Error: pow!
      at null._onTimeout (test.js:6:15)

  2)  failing expect:

      Uncaught AssertionError: expected 1 to equal 2
      + expected - actual

      -1
      +2

      at null._onTimeout (test.js:13:22)

The stack traces point to the correct code lines. If the exceptions happened deeper, the stack would be fuller.

When Mocha cannot report what went wrong exactly, that's usually because there is intervening code that swallows the exception that was raised. Or when you use promises the problem may be that someone forgot to call a method that indicates whether the promise is supposed to be completely processed and unhandled exceptions should be thrown. (How you do this depends on the promise implementation you use.)

It won't, it's a shame. It has no way to know that your callback is executing. It's an easier way to do asynchronous testing, where you just tell the test when you are finished. The downside, as you have noticed, is that errors in asynchronous callbacks won't be detected. Nevermind, Mocha hooks to process.on('uncaughtException',...) as mentioned by Louis . Note that if you use done instead of waitsFor and runs in jasmine, then you will have the problem .

Other frameworks like js-test-driver force to you wrap callbacks so the the testing framework can put a try catch around your callbacks (and you don't need to call done). Your test would look like the following:

var AsynchronousTest = AsyncTestCase('User');

AsynchronousTest.prototype.testSave = function(queue) {
  queue.call('Saving user', function(callbacks) {
    var user = new User('Luna');
    user.save(callbacks.add(function(err) {
       if (err) throw err;
       // Run some asserts
    }));
  }); 
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM