
Using Node’s debugger in Jest tests
If you ever sit in on a class or pairing session with me, you might hear me telling a student to stop using console.log() in their React debugging process, and to instead rely on debugger, via their Chrome DevTools. There is a good reason for this. console.log generally allows you to ask one question about your code, ‘What is this thing right now?’.
debugger on the other hand, allows you to ask many questions; ‘What is the state of my component right now?’, ‘Are my local variables what I think they are?`, ‘Is that click event getting passed properly to my event handler?’
Generally, when you make the transition to using debugger, you’re able to work faster in your React development process, because you’re able to collect more data points about what is happening. Information is power.
However, when we’re writing tests, suddenly our code is no longer running in the browser, and we lose access to our standard debugger. Particularly when you’re new, this is frustrating, as you’d like to be able to use the same tools to debug tests, as you do your production code.
Fortunately, there is a way to do just that!
Opening the chrome inspector
First off, we’re going to want to have the node inspector open. You could use any one of several technologies to connect to the Node.js inspector, but since I already mentioned using the Chrome DevTools, let’s use that. You can open up the inspector by navigating to chrome://inspect in your Chrome browser. That should take you to a window that looks like this:
Click on the link Open dedicated DevTools for Node, and you should the DevTools you’re familiar with from react development.
Debugging some tests
We’ll need some code to debug. You could use any Jest test suite the way I’m going to here, but if you’d like to follow along, feel free to use this repository, a minimal Tic-Tac-Toe implementation that I created.
We need to tell Node to run Jest with the inspector agent enabled, which with can accomplish with the --inspect flag. Additionally, we should tell Jest to run the tests serially, rather than in a pool, so that we can be confident the tests are executed in the order we expect. This can be done by running Jest with the --runInBand flag.
If you’re using the example repository, you can see I’ve added this all as an npm script, testDebug to the package.json:
{
"name": "jesting",
"version": "1.0.0",
"description": "Test module for writing a variety of jest tests",
"main": "index.js",
"scripts": {
"test": "jest",
"testDebug": "node --inspect node_modules/.bin/jest --runInBand"
},
"author": "Will Mitchell <wvmitchell@gmail.com>",
"license": "ISC",
"devDependencies": {
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
"babel-preset-env": "^1.7.0",
"jest": "^23.5.0"
},
"dependencies": { "react": "^16.4.2" }
}Now, when I run the tests with the new script, it will respect any debugger commands it comes across, and we’ll be able to ask questions about our code in the inspector.
Additionally, I can place my debugger statements in either the tests, or my code, it will stop execution in both places. If you’re trying to determine why a specific test isn’t working properly, this can be extra helpful:
// index.js checkRowWinner = () => {
const xWins = this.board.some(row => row.every(cell => cell === 'X'))
const oWins = this.board.some(row => row.every(cell => cell === 'O')) debugger if(xWins) {
return 'X'
} else if(oWins) {
return 'O'
}
}// index.test.js it.only('knows if there is a row win', () => {
game.board = [['X', 'X', 'X'],[null, null, null],[null, null, null]]
const expected = 'Game over. X wins!'
const result = game.status() debugger expect(result).toEqual(expected)
})
Final Thoughts
Writing tests doesn’t need to be harder that writing code, and committing to doing so will make your code more robust, scalable, and maintainable. Make it easier on yourself by using the tools you’re already familiar with.
Happy hacking!
Originally published at wvmitchell.net on September 1, 2018.
