Using Visual Studio Code to debug Jest based unit tests

The need

Jest has become the de facto standard for building unit tests. Writing tests in JavaScript / TypeScript is fine, but old hands coming from the .net/java/karma approaches will soon start to miss something... How on God's green earth can I debug this? The answer is using Visual Studio Code !! You can just place breakpoints, choose whether or not to make a single run, enable watch mode, or even only execute the tests of the current opened file.

That's great, so how can I integrate Visual Studio Code debugging capabilities in my Jest based test suite? Just by setting up a launch config file. In this post, you will learn how to do that.

What we are going to cover

The scenarios that we are going to cover:

  • How to config Visual Studio Code debugging on a project created using create-react-app.
  • How to config Visual Studio Code debugging on a project created from scratch and:

    • Jest configuration is included in the package.json file.
    • Jest configuration has been isolated in a separate jest config file.

Let's get started!

TL;DR

If you are in a hurry and just need the config files, here you are:

If you want to learn how to configure this step by step, keep on reading :).

Setting up configuration for a create-react-app based project

Let's say you have created your project using the create-react-app Facebook helper. In this case, you don't have direct access to jest, so you have to execute react-scripts to get your tests working.

To follow this guide:

Step 1 Enable debugging in our project

First of all, let's enable debugging on our project, in order to do that:

  • We will click on the debug icon (left hand sidebar).
  • Click on the add configuration option in the dropdown list.
  • Choose nodejs (jest runs under node).
  • A brand new launch.json file will be displayed.

enable-debug

We got a launch.json file under the folder .vscode including a default implementation:

  • In the next step we will replace the content of this file with a new one that will let us integrate the editor with Jest.
  • If you are working on a git repository, check your .gitIgnore file and make sure this .vscode folder is not ignored.

Step 2 Configuring jest test debugging single run

Let's replace the default config file created by VS Code and place the following one:

/.vscode/launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug tests single run",
      "type": "node",
      "request": "launch",
      "env": { "CI": "true" },
      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts",
      "args": ["test", "--runInBand", "--no-cache"],
      "cwd": "${workspaceRoot}",
      "protocol": "inspector",
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    }
  ]
}

A little bit of background on this configuration:

  • name: We just provide a friendly name for this configuration.
  • type: Jest runs under node, that's why we set up this value.
  • request: launch a program.
  • env: we set up an environment variable CI: true in order instruct jest to stop the execution once the test battery has been executed.
  • runtimeExecutable: we want to run react-scripts
  • Arguments:

    • test: we are indicating that we want to launch tests.
    • runInBand: run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. Normally Jest parallelizes test runs across processes but it is hard to debug many processes at the same time.
    • no-cache: Disable the cache, Jest caches transformed module files to speed up test execution (in some scenarios this can lead to issues when debugging).
    • watchAll: Watch files for changes and rerun all tests when there is an update on any of the files. Since we are performing a single run we set this flag to false.

Step 3 Debugging our project

Now we can debug our project:

  • Place breakpoints in your code.
  • Click on the debug icon (sidebar).
  • Pick up from the options "Debug Test Single run"
  • Click on the play icon.

debugging

Step 4 Configuring jest test debugging watch mode

Sometimes, rather than launching the tests once, you just want to keep the debugging daemon alive and watch for any file change to run them again.

We only need to remove the environment variable entry CI: true from the config:

      "request": "launch",
-      "env": { "CI": "true" },
      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts",

By default react-scripts will work on watch mode but we can explicitly set this value:

      "args": [
               "test",
               "--runInBand",
               "--no-cache",
+               "--watchAll"
               ],

The resulting launch.json that we get (appended the new configuration in the launch.json file):

/.vscode/launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug tests single run",
      "type": "node",
      "request": "launch",
      "env": { "CI": "true" },
      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts",
      "args": ["test", "--runInBand", "--no-cache"],
      "cwd": "${workspaceRoot}",
      "protocol": "inspector",
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
+    }
-    },
+    {
+      "name": "Debug tests watch mode",
+      "type": "node",
+      "request": "launch",
+      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts",
+      "args": ["test", "--runInBand", "--no-cache", "--watchAll"],
+      "cwd": "${workspaceRoot}",
+      "protocol": "inspector",
+      "console": "integratedTerminal",
+      "internalConsoleOptions": "neverOpen"
+    }
  ]
}

Step 5 Only run currently opened file tests

The two prior configurations were great, but what happens if we've got a big project that contains lots of tests? Sometimes we just want to rerun the tests defined on the current, open file that we are editing.

We only need to mention the file that is currently opened, the args section:

"args": [
    "test",
+    "${fileBasenameNoExtension}",
    "--runInBand",
    "--no-cache",
    "--watchAll=true"
],

Full configuration file:

./.vscode/launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Jest Debug Tests Single Run",
      "type": "node",
      "request": "launch",
      "env": { "CI": "true" },
      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts",
      "args": ["test", "--runInBand", "--no-cache"],
      "cwd": "${workspaceRoot}",
      "protocol": "inspector",
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    },
    {
      "name": "Jest Debug tests watch mode",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts",
      "args": ["test", "--runInBand", "--no-cache", "--watchAll"],
      "cwd": "${workspaceRoot}",
      "protocol": "inspector",
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    },
+    {
+      "name": "Jest Debug opened file",
+      "type": "node",
+      "request": "launch",
+      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts",
+      "args": ["test", "${fileBasenameNoExtension}", "--runInBand", "--no-cache", "--watchAll"],
+      "cwd": "${workspaceRoot}",
+      "protocol": "inspector",
+      "console": "integratedTerminal",
+      "internalConsoleOptions": "neverOpen"
+    },
  ]
}

Setting up config for a project created from scratch

Step 1 Enabling debugging in our project

First of all, let's enable debugging in our project. In order to do that:

  • We will click on the debug icon (left hand sidebar).
  • Click on the add configuration option in the dropdown list.
  • Choose nodejs (jest runs under node).
  • A new launch json file will be dispayed.

enable-debug

Step 2 Configuring jest test debugging single run

Let's replace the default config file created by VS Code and place the following content:

/.vscode/launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Jest single run all tests",
      "program": "${workspaceRoot}/node_modules/jest/bin/jest.js",
      "args": ["--verbose", "-i", "--no-cache"],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    }
  ]
}

This will work fine if we have all the jest configuration in the package.json file, but what if we have a separate jest config file? We need to set that up by adding a couple of entries to the args section:

  • -c to indicate the path to the jest.json config file.
  • The path to the jest config file (in our case is ./config/test/jest.json if it's located in the root folder it would be ./jest.json).

The final resulting config in this case would look something like this:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Jest single run all tests",
      "program": "${workspaceRoot}/node_modules/jest/bin/jest.js",
      "args": [
+        "-c",
+        "./config/test/jest.json",
        "--verbose",
        "-i",
        "--no-cache"
      ],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    }
  ]
}

Step 3 Debugging our project

Now we can debug our project:

  • Place breakpoints in your code.
  • Click on the debug icon (sidebar).
  • Pick up from the options "Debug Test Single run"
  • Click on the play icon.

debugging

Step 4 Configuring jest test debugging watch mode

Sometimes, rather than launching the tests once, you just want to keep the debugging daemon alive and watch for any file change to run them again.

We only need to add the parameter watchAll, something like:

      "args": [
               "--runInBand",
               "--no-cache",
+               "--watchAll"
               ],

The resulting launch.json that we get (appended the new configuration in the launch.json file):

/.vscode/launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Jest single run all tests",
      "program": "${workspaceRoot}/node_modules/jest/bin/jest.js",
      "args": [
        "--verbose",
        "-i",
        "--no-cache"
      ],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    },
+    {
+      "type": "node",
+      "request": "launch",
+      "name": "Jest watch all tests",
+      "program": "${workspaceRoot}/node_modules/jest/bin/jest.js",
+      "args": [
+        "--verbose",
+        "-i",
+        "--no-cache",
+        "--watchAll"
+      ],
+      "console": "integratedTerminal",
+      "internalConsoleOptions": "neverOpen"
+    },
  ]
}

Step 5 Configuring jest test debugging currently opened file

The two prior configurations were great, but what happens if we've got a big project that contains lots of tests? Sometimes we just want to rerun the tests defined on the current, open file that we are editing.

We only need to mention the file that is currently opened, the args section:

"args": [
    "test",
+    "${fileBasenameNoExtension}",
    "--runInBand",
    "--no-cache",
    "--watchAll=true"
],

Full configuration file:

./.vscode/launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Jest single run all tests",
      "program": "${workspaceRoot}/node_modules/jest/bin/jest.js",
      "args": [
        "--verbose",
        "-i",
        "--no-cache"
      ],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    },
    {
      "type": "node",
      "request": "launch",
      "name": "Jest watch all tests",
      "program": "${workspaceRoot}/node_modules/jest/bin/jest.js",
      "args": [
        "--verbose",
        "-i",
        "--no-cache",
        "--watchAll"
      ],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
-    }
+    },
+    {
+      "type": "node",
+      "request": "launch",
+      "name": "Jest watch current file",
+      "program": "${workspaceFolder}/node_modules/jest/bin/jest",
+      "args": [
+        "${fileBasename}",
+        "--verbose",
+        "-i",
+        "--no-cache",
+        "--watchAll"
+      ],
+      "console": "integratedTerminal",
+      "internalConsoleOptions": "neverOpen"
+    }
  ]
}

Demos source code

You can find all the demo material in the Github Repo jest-vs-code-debugging-example

Each folder contains a starting point and the solution implemented.

Resources

Official facebook create-react-app guide for debugging tests (including Chrome configuration).

Microsoft has got a great guide on how to configure jest debugging.

Wrapping up

Being able to debug jest unit tests by using our favourite editor can boost our productivity, and setting up all the plumbing is a matter of minutes if you get the right config file. I hope you enjoyed this post.

About Basefactor

We are a team of JavaScript experts. If you need coaching or consultancy services, don't hesitate to contact us.

Doers/

Location/

C/ Pintor Martínez Cubells 5 Málaga (Spain)

General enquiries/

info@lemoncode.net

+34 693 84 24 54

Copyright 2018 Basefactor. All Rights Reserved.