Skip to content

Instantly share code, notes, and snippets.

@emmanuelnk
Forked from boneskull/README.md
Created April 17, 2020 07:36
Show Gist options
  • Save emmanuelnk/1e235d52e06b91c184910fe01419a399 to your computer and use it in GitHub Desktop.
Save emmanuelnk/1e235d52e06b91c184910fe01419a399 to your computer and use it in GitHub Desktop.
example of how to debug mocha v4 if hanging

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.

'use strict';
const {createHook} = require('async_hooks');
const {stackTraceFilter} = require('mocha/lib/utils');
const allResources = new Map();
// this will pull Mocha internals out of the stacks
const filterStack = stackTraceFilter();
const hook = createHook({
init(asyncId, type, triggerAsyncId) {
allResources.set(asyncId, {type, triggerAsyncId, stack: (new Error()).stack});
},
destroy(asyncId) {
allResources.delete(asyncId);
}
}).enable();
global.asyncDump = module.exports = () => {
hook.disable();
console.error(`
STUFF STILL IN THE EVENT LOOP:`)
allResources.forEach(value=> {
console.error(`Type: ${value.type}`);
console.error(filterStack(value.stack));
console.error('\n');
});
};
'use strict';
const net = require('net');
const assert = require('assert');
describe('how to debug Mocha when it hangs', function () {
before(function (done) {
const server = net.createServer();
server.listen(10101, done);
});
it('should complete, but Mocha should not exit', function(done) {
const sock = net.createConnection(10101, () => {
assert.deepEqual(sock.address().family, 'IPv4');
done();
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment