Here's an example of how to debug Mocha v4 if it hangs.
Ensure you're using a Node.js 8 or newer (or any version with async_hooks support).
If you run your test, you'll notice it hangs:
$ mocha test.js
how to debug Mocha when it hangs
✓ should complete, but Mocha should not exit
1 passing (25ms)
You can include a module like async-dump.js
to print information about what's happening. First, add this hook somewhere in the describe
block:
after(function () {
global.asyncDump();
});
Then, you can --require
async-dump.js
with Mocha:
$ mocha --require async-dump test.js
how to debug Mocha when it hangs
✓ should complete, but Mocha should not exit
STUFF STILL IN THE EVENT LOOP:
Type: SIGNALWRAP
Error
at AsyncHook.init (async-dump.js:12:62)
at Signal.emitInitNative (async_hooks.js:458:43)
at process.<anonymous> (internal/process.js:209:20)
at startup (bootstrap_node.js:201:16)
at bootstrap_node.js:626:3
Type: TCPWRAP
Error
at AsyncHook.init (async-dump.js:12:62)
at TCP.emitInitNative (async_hooks.js:458:43)
at createServerHandle (net.js:1267:14)
at Server.setupListenHandle [as _listen2] (net.js:1310:14)
at listenInCluster (net.js:1391:12)
at Server.listen (net.js:1474:7)
at Context.<anonymous> (debug-hanging-mocha.js:9:12)
Type: TCPWRAP
Error
at AsyncHook.init (async-dump.js:12:62)
at TCP.emitInitNative (async_hooks.js:458:43)
at Socket.connect (net.js:997:40)
at Object.connect (net.js:104:35)
at Context.<anonymous> (debug-hanging-mocha.js:17:22)
Type: DNSCHANNEL
Error
at AsyncHook.init (async-dump.js:12:62)
at ChannelWrap.emitInitNative (async_hooks.js:458:43)
at new Resolver (dns.js:249:20)
at dns.js:380:25
at NativeModule.compile (bootstrap_node.js:614:7)
at NativeModule.require (bootstrap_node.js:559:18)
at lazyDns (net.js:1376:11)
at lookupAndConnect (net.js:1026:15)
at Socket.connect (net.js:1019:5)
at Object.connect (net.js:104:35)
at Context.<anonymous> (debug-hanging-mocha.js:17:22)
Type: TCPWRAP
Error
at AsyncHook.init (async-dump.js:12:62)
at TCP.emitInitNative (async_hooks.js:458:43)
Type: Immediate
Error
at AsyncHook.init (async-dump.js:12:62)
at emitInitNative (async_hooks.js:458:43)
at emitInitScript (async_hooks.js:361:3)
1 passing (25ms)
These are not Errors; everything that's printed above are async tasks that have been "started" but not "finished".
From the above, you will notice that the TCPWRAP
resource(s) are interesting. Within the stack trace, you can see where exactly these happen in your code:
Type: TCPWRAP
Error
at AsyncHook.init (async-dump.js:12:62)
at TCP.emitInitNative (async_hooks.js:458:43)
at createServerHandle (net.js:1267:14)
at Server.setupListenHandle [as _listen2] (net.js:1310:14)
at listenInCluster (net.js:1391:12)
at Server.listen (net.js:1474:7)
at Context.<anonymous> (debug-hanging-mocha.js:9:12)
On line 9, we're listening on a port, but we never shut down the server.
async-dump.js
could certainly be improved upon, but this is a rough guide.
The Node.js inspector (use --inspect-brk
, etc.) will also give you similar information, but it's not so easily filtered.
why-is-node-running can also be used with some fiddling (do node --expose-internals /path/to/_mocha test.js
then require('why-is-node-running')()
at the top of test.js
), but YMMV.
...and if you're spinning your wheels, nobody is telling you that you can't just add
--exit
and move on to something else!