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 );
}
}