Debugging mocha tests in a Docker container using Visual Studio Code

Written on April 10, 2016

In a current Node.js / Docker customer project I had to debug some mocha tests because Docker containers use UTC and you know: that timezone stuff...

In the end, I was debugging my mocha tests using WebStorm and node-inspector inside the containers and got stuff running.

As I have been playing around with Visual Studio Code recently and debugging Node.js in Docker containers as well as debugging mocha tests (both using VS Code), this thought came up: "how would I be able to debug mocha tests using Visual Studio Code in Docker containers?"

In the end, it worked out to be pretty staight forwarded.

Make sure to have ES6 enabled in jsconfig.json for the sample code:

{
    "compilerOptions": {
        "target": "ES6",
        "module": "commonjs"
    }
}

First, I have a little express application inside ./index.js:

const express = require('express');
const app = express();

app.get('/', (req, res) => {
    res.status(200).send('hello world');
})

app.listen(3000);

This application isn't really required for our tests but my aforementioned scenario was like it. Furthermore we need a process that prevents our container from exiting after he has been started, so we'll stick with that little app.

Next, we have a Dockerfile:

FROM node:4.2.3

EXPOSE 3000
EXPOSE 5858
COPY . /app
WORKDIR /app

RUN cd /app; npm install
CMD ["node","index.js"]

As this post is about debugging mocha tests, here's our little test (./test/test.js) we want to debug inside the container:

const assert = require('assert');

describe('Truth', () => {
    it('should be told', (done) => {
        assert.equal(true, true)
        done()
    })
})

The idea of how I want to debug this test inside a Docker container is as follows:

  1. Start the container
  2. Run mocha inside the container with --debug-brk at port 5858 to wait for Visual Studio Code to attach to the debugger
  3. Attach Visual Studio code and debug the test

There is another viable method:

  1. Don't use our app in index.js
  2. Call mocha with --debug-brk and port 5858 in the Dockerfile CMD
  3. Start the container
  4. Attach Visual Studio code and debug the test

As I don't want to have a container dedicated for debugging of mocha tests, I prefer the first version.

Starting the container consists of building the image and running the container. This can be easily automated using nodemon, grunt-watch or something similar. You could even use a Docker Volume and not restart the container at all.

Whatever automation you choose, here are the commands to build and run the container:

$ docker build -t vscodemocha .
$ docker run -d --name vscodemocha -p 3000:3000 -p 5858:5858 vscodemocha

With our container being up and running, we can configure Visual Studio Code to run mocha inside the container.

The Visual Studio Code attach Configuration allows to provide a preLaunchTask which is a task that can be defined inside the tasks configuration in tasks.json.

So we'll simply create a runmochaindocker task based on the bash command in tasks.json (inside the .vscode folder):

{
    "version": "0.1.0",
    "isShellCommand": true,
    "command": "bash",
    "args": [
        "-c"
    ],
    "tasks": [
        {
            "taskName": "runmochaindocker",
            "showOutput": "always",
            "suppressTaskName": true,
            "args": [
                "docker exec -i vscodemocha bash -c \"./node_modules/mocha/bin/mocha --debug-brk=5858 -t 10000 test/test.js\""
            ]
        }
    ]
}

Having that done, we're now referencing that task in the attach configuration in launch.json:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Attach to Mocha in Docker",
            "type": "node",
            "request": "attach",
            "port": 5858,
            "preLaunchTask": "runmochaindocker",
            "address": "localhost",
            "restart": false,
            "sourceMaps": false,
            "outDir": null,
            "localRoot": "${workspaceRoot}/",
            "remoteRoot": "/app/"
        }
    ]
}

As already said, runmochaindocker now is defined as a preLaunchTask. And as already described in my recent post on debugging a Node.js app in a Docker container, I set localRoot and remoteRoot accordingly.

Guess what? We're done!

Just hit F5 now, and you'll get this:

Then, hit F5 again:

(To be honest, I'm not sure why the debugger breaks here - but we can ignore that).

Hit F5 one last time, and we're there: debugging our mocha test in a Docker container:

Happy debugging!