Uncaught exceptions is really dangerous thing, that may break your mochajs tests queue.
Let's consider code example with async calls and uncaught exceptions.
/* sample.js */
var sleep = timeout => {
return new Promise(resolve => {
setTimeout(() => {
console.log(`I was sleeping ${timeout} ms`);
resolve();
}, timeout);
});
};
var error = timeout => {
setTimeout(() => {
throw new Error("BOOM!!!");
}, timeout);
};
describe("scope", () => {
it ("test #1", async () => {
error(1000);
await sleep(1000);
});
it ("test #2", async () => await sleep(1000));
it ("test #3", async () => {
error(1000);
await sleep(1000);
});
it ("test #4", async () => await sleep(1000));
it ("test #5", async () => await sleep(1000));
it ("test #6", async () => await sleep(1000));
});Execute it and get something really strange in report:
$ mocha sample.js
scope
1) test #1 # in console it colored as red (failed)
I was sleeping 1000 ms
√ test #1 (1012ms)
2) test #3 # in console it colored as red (failed)
I was sleeping 1000 ms
I was sleeping 1000 ms
√ test #4
√ test #4
I was sleeping 1000 ms
I was sleeping 1000 ms
I was sleeping 1000 ms
√ test #6 (1002ms)
√ test #6 (1002ms)
√ test #6 (1003ms)
6 passing (3s)
2 failing
1) scope
test #1:
Uncaught Error: BOOM!!!
at Timeout.setTimeout [as _onTimeout] (proba.js:12:15)
2) scope
test #3:
Uncaught Error: BOOM!!!
at Timeout.setTimeout [as _onTimeout] (proba.js:12:15)
6 passing (3s)
2 failing
1) scope
test #1:
Uncaught Error: BOOM!!!
at Timeout.setTimeout [as _onTimeout] (proba.js:12:15)
2) scope
test #3:
Uncaught Error: BOOM!!!
at Timeout.setTimeout [as _onTimeout] (proba.js:12:15)
6 passing (3s)
2 failing
1) scope
test #1:
Uncaught Error: BOOM!!!
at Timeout.setTimeout [as _onTimeout] (proba.js:12:15)
2) scope
test #3:
Uncaught Error: BOOM!!!
at Timeout.setTimeout [as _onTimeout] (proba.js:12:15- Pay attention, that
test #1is mentioned twice: as passed and as failed! test #2&test #5are absent in report!- You may see concurrent printing of messages
I was sleeping 1000 ms, 1 time, 2 times, 3 times.
Let's see why it happens.
The problem, that by default mochajs processes uncaught exceptions: https://github.com/mochajs/mocha/blob/master/lib/runner.js#L698. And if such exception happens, mochajs fails currently executed test. And it doesn't matter whether such exception happens in current test or was born many tests ago (due to unstopped timer, for example). This processing is implemented via listener, and in above example very interesting things happen:
- In
test #1on one handuncaught exceptionhappens after 1 second and its processing starts. On the other hand,test #1waits forsleepfinishing 1 second and thenmochajsmarks it as passed. - Due to async of
JavaScript, there are two concurrentlytest #1processors, that leads totest #1reporting twice. - More over, since there are two places, which emit events to start new test execution. And factically, there are two tests queues.
- In
test #3previous situation repeats. And there are 3 concurrently executed tests queues! The evidence is a number of printed messagesI was sleeping 1000 ms.
How to fix
Simple and working variant is to suppress uncaught exception.
var Mocha = require("mocha");
Mocha.Runner.prototype.uncaught = function (err) {
logger.error("UNCAUGHT ERROR", err);
};It's better to get one failed test and in test finalizers to close all descriptors, to stop proxies, to kill processes and so on, than to get fully failed tests queue in nightly build.
That's why mechanism to suppress uncaught exceptions is enabled by default in GlaceJS. But it may be disabled with option --allow-uncaught.