Is there a way to get Chai working with asynchronous Mocha tests?

javascript, mocha.js, unit-testing

Solution

Your asynchronous test generates an exception, on failed `expect()`ations, that cannot be captured by `it()` because the exception is thrown outside of `it()`'s scope.

The captured exception that you see displayed is captured using `process.on('uncaughtException')` under node or using `window.onerror()` in the browser.

To fix this issue, you need to capture the exception within the asynchronous function called by `setTimeout()` in order to call `done()` with the exception as the first parameter. You also need to call `done()` with no parameter to indicate success, otherwise mocha would report a timeout error because your test function would never have signaled that it was done:

window.expect = chai.expect;

describe( 'my test', function() {
  it( 'should do something', function ( done ) {
    // done() is provided by it() to indicate asynchronous completion
    // call done() with no parameter to indicate that it() is done() and successful
    // or with an error to indicate that it() failed
    setTimeout( function () {
      // Called from the event loop, not it()
      // So only the event loop could capture uncaught exceptions from here
      try {
        expect( true ).to.equal( false );
        done(); // success: call done with no parameter to indicate that it() is done()
      } catch( e ) {
        done( e ); // failure: call done with an error Object to indicate that it() failed
      }
    }, 100 );
    // returns immediately after setting timeout
    // so it() can no longer catch exception happening asynchronously
  }
}

Doing so on all your test cases is annoying and not DRY so you might want to provide a function to do this for you. Let's call this function `check()`:

function check( done, f ) {
  try {
    f();
    done();
  } catch( e ) {
    done( e );
  }
}

With `check()` you can now rewrite your asynchronous tests as follows:

window.expect = chai.expect;

describe( 'my test', function() {
  it( 'should do something', function( done ) {
    setTimeout( function () {
      check( done, function() {
        expect( true ).to.equal( false );
      } );
    }, 100 );
  }
}

Problem

I'm running some asynchronous tests in Mocha using the Browser Runner and I'm trying to use Chai's expect style assertions: ``` window.expect = chai.expect; describe('my test', function() { it('should do something', function (done) { setTimeout(function () { expect(true).to.equal(false); }, 100); } } ``` This doesn't give me the normal failed assertion message, instead I get: ``` Error: the string "Uncaught AssertionError: expected true to equal false" was thrown, throw an Error :) at Runner.fail (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3475:11) at Runner.uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3748:8) at uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3778:10) ``` So it's obviously catching the error, it's just not displaying it correctly. Any ideas how to do this? I guess I could just call "done" with an error object but then I lose all the elegance of something like Chai and it becomes very clunky...

Original source