When running tests that utilize domains for error handling, Mocha still appears to be throwing an error even if a domain handler inside a library should have caught the erro
nodejs domains do definitely catch synchronous errors
See this simple test case
var domain = require("domain");
var d = domain.create();
d.on("error", function() {
console.log("domain caught");
});
d.run(function() {
throw new Error("foo");
});
// result: domain caught
EDIT: Since writing this answer I have written a blog post describing what is going on with domains and try catch and whether you can use domains as a wholesale replacement for try catch. It summarises most of what has been discussed here.
http://www.lighthouselogic.com/node-domains-as-a-replacement-for-try-catch/
ORIGINAL ANSWER:
Actually there is a problem with Mocha.
I wrote the following test function:
function error(callback){
var d = domain.create().on('error', function(err){
console.log("Caught Error in Domain Handler")
return callback(err);
});
d.enter();
throw new Error("TestError");
d.exit();
}
Then I wrote a simple test without mocha:
error(function(err){
if(err)
{
console.log("Error was returned");
}else
{
console.log("Error was not returned")
}
})
The output I received was:
Caught Error in Domain Handler
Error was returned
When I tested using Mocha:
describe('Domain Tests', function(){
it('Should return an error when testing', function(done){
error(function(err){
if(err)
{
console.log("Error was returned");
}else
{
console.log("Error was not returned")
}
return done();
})
});
});
I received the following output:
․
0 passing (4ms)
1 failing
1) Domain Tests Should return an error when testing:
Error: TestError
at error (/Users/bensudbury/Documents/node_projects/testMochaDomains/test.js:9:11)
at Context.<anonymous> (/Users/bensudbury/Documents/node_projects/testMochaDomains/testMocha.js:6:3)
at Test.Runnable.run (/usr/local/share/npm/lib/node_modules/mocha/lib/runnable.js:194:15)
at Runner.runTest (/usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:358:10)
at /usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:404:12
at next (/usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:284:14)
at /usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:293:7
at next (/usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:237:23)
at Object._onImmediate (/usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:261:5)
at processImmediate [as _immediateCallback] (timers.js:330:15)
As you can see: the domain error handler was short circuited.
This problem seems to be related to the following issues:
https://github.com/visionmedia/mocha/issues/513
While the Node issue has been closed, the issue in mocha is still open.
The workaround that was suggested at: https://gist.github.com/mcollina/4443963 didn't resolve the issue in this case.
I dug through the code of Mocha and found that the problem occurs because mocha wraps the tests in a try catch block. This means that the exception is caught and never sent to the uncaughtException or _fatalException handler depending upon the version of node that you are using.
Your workaround is good, but nodejs domains do definitely catch synchronous errors so I wouldn't change your code but instead change your test. Your new test should look like:
describe("test", function() {
it("should succeed", function(done) {
process.nextTick(function(){
var foo = require("./foo.js");
foo(function() {
console.log("done");
done();
});
})
});
});
I haven't tested this code, but similar code for my example works properly:
it('Should return an error when testing', function(done){
process.nextTick(function(){
error(function(err){
if(err)
{
console.log("Error was returned");
}else
{
console.log("Error was not returned")
}
return done();
});
})
});
I have added a comment to the end of the issue in Mocha to see if it can be resolved:
https://github.com/visionmedia/mocha/issues/513
Found the problem. NodeJS domains catch synchronous errors but the event continues to bubble to a try/catch. If you wrap a domain.run()
in a try/catch
then the domain error handler AND the catch will be executed.
Thus, it seems the best practice is to use process.nextTick inside all domain.run()
. This is shown in the docs example, but isn't expressed as explicitly as I would prefer.
Example:
d.run(function() {
process.nextTick(function() {
// do stuff
});
});
In this case, the flaw is not in Mocha.
Proof of NodeJS domains not catching synchronous errors in try/catch: https://gist.github.com/owenallenaz/7141699