Setup Jest for Angular with Debugging in Visual Studio Code
Published: 2019-12-09 19:31:41

> This article aims to describe a really fast step-by-step way to setup [Jest](https://jestjs.io) testing for [Angular](https://angular.io/) projects including [debugging](https://code.visualstudio.com/Docs/editor/debugging) capabilities of [Visual Studio Code](https://code.visualstudio.com/).

# Overview
- [Why Jest?](#jest)
- [Prerequisites](#pre)
- [Removal of Karma (and Jasmine)](#remove)
- [Setting up Jest](#setup)
- [Configure Debugging in VS Code](#debugging)
- [Outlook and References](#outlook)

I always thought that no one will ever read my blog posts. However, I myself will surely read my own as long as I haven't completely automated the following steps. The upcoming information is already present online but varies and is distributed over several sources[*](#references). My goal is to describe everything that is needed in one single post.

# Why Jest?<a name="jest"></a>
The default test runner for Angular applications is [Karma](https://karma-runner.github.io/latest/index.html) in combination with [Jasmine](https://jasmine.github.io/) as test framework. In recent times I often prefer [Jest](https://jestjs.io) as testing framework. In my opinion it has some advantages over the default setup: It is headless out-of-the-box, less configuration is needed and it has some handy features like snapshot testing. Since Jest is headless which means that it won't spawn a browser window where you can open Chrome Dev Tools, it can get cumbersome to use console-logs when trying to debug while testing. The debug logs would spam the terminal and you will probably have to scroll a lot to find the outputs you are looking for. At this point you should think about using the great [debugging](https://code.visualstudio.com/Docs/editor/debugging) features of your editor.

# Some Prerequisites<a name="pre"></a>
This guide assumes that you are using Angular in version 8 (as it uses the new [CLI builders](https://angular.io/guide/cli-builder)). Moreover, I assume that we won't use [Protractor](https://www.protractortest.org) as E2E testing framework. We're already living in heaven and may be using [Cypress](https://cypress.io) later. Therefore, we can safely remove anything related to Jasmine.

If you don't have an Angular application already, you may create one like you would normally do with the [Angular CLI](https://angular.io/cli/new):

```sh
ng new <app-name>
```

# The Cleanup<a name="remove"></a>
When it comes to source code or configuration, I am a huge friend of *less is more*. Any code snippet or configuration that is not needed or commented out can be removed. Our plan is to remove any Karma and Jasmine related dependencies and entities to always have a clean project.

## Remove Protractor and Jasmine *(Optional)*
If you really want to use Protractor for your end-to-end tests you can skip this part. Otherwise, we will remove everything related to the E2E tests. If we have done this, we can remove everything related to Jasmine, too.

Remove the following because we won't use it anymore:
- `/e2e` folder containing all your end-to-end tests and configuration
- `e2e` section from `angular.json`
- `e2e` entry in the `README.md`
- `e2e` command from the `scripts` in the `package.json`

Now we can safely uninstall Protractor and Jasmine dependencies:
```sh
npm uninstall protractor
npm uninstall jasmine-core jasmine-spec-reporter @types/jasmine @types/jasminewd2
```

## Remove Karma
Since we will use Jest for our entire testing we can now remove everything that is related to Karma.

Uninstall Karma dependencies:
```sh
npm uninstall karma karma-chrome-launcher karma-coverage-istanbul-reporter
karma-jasmine karma-jasmine-html-reporter
```

As we don't use Karma, we can remove the following files:
- `karma.conf.js`
- `src/test.ts`

# The Setup<a name="setup"></a>
We cleaned up our project from any unused resources and will now setup our toolchain to run unit tests with Jest.

## Add Jest
We can start by installing Jest, it's types and new Angular builder that is used to run the Jest tests:

```bash
npm install --save-dev jest @types/jest @angular-builders/jest
```

## Adjust Angular CLI Configuration
Replace the previous Karma `builder` in the `test` section of the `angular.json` with the new Jest builder:
```json
"test": {
  "builder": "@angular-builders/jest:run",
  "options": { 
    // The options and everything in here can be removed.
  }
```

## Adjust TypeScript Configurations
In your configuration for the tests (`tsconfig.spec.json`) change the entry `jasmine` in the `types` array to `jest` and remove the file `src/test.ts` from the `files` array. Set `emitDecoratorMetadata` and `esModuleInterop` to `true`. The first one is needed for Angular's dependency injection to work this Jest, the latter one is [suggested](https://github.com/kulshekhar/ts-jest/wiki/Troubleshooting#commonjs-compatibility) by a Jest warning when running your tests.
```json
{
  // ...
  {
    // ...
    "types": [
      "jest",
      "node"
    ],
    "emitDecoratorMetadata": true,
    "esModuleInterop": true
  },
  "files": ["src/polyfills.ts"]
  // ...
}
```

Lastly in your main base configuration `tsconfig.json` also change the `types` entry from `jasmine` to `jest`.

## Add Minimal Jest Configuration *(Optional)*
When running Jest it complains that it cannot find a configuration file. Since I don't like such warnings we take this opportunity to add a minimal configuration that also setups up the code coverage format. I want the coverage to output a nice HTML site as an overview. You can use one of the [Istanbul reporters](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib) here.

You can create the file `jest.config.js` in the root of your project and add the following contents:
```js
module.exports = {
  coverageReporters: ['html', 'text', 'text-summary']
};
```

## Adjust NPM Test Scripts *(Optional)*
I like to define two npm scripts to run my tests. The `test` script runs all tests and produces the code coverage. The `test:watch` script doesn't produce code coverage but watches for file changes (using the integrated Jest watch mode).

You can add the following to your `package.json`:
```json
"scripts": {
  // ...
  "test": "ng test --coverage",
  "test:watch": "ng test --watch",
  // ...
}
```

# Configure Debugging in VS Code<a name="debugging"></a>
As debugging via console-logs can be tedious, especially when your unit tests are running in headless mode, we want to use the great [debugging features of Visual Studio Code](https://code.visualstudio.com/Docs/editor/debugging). The goal is to have two launch scripts to run and debug all tests and only tests that belong to the currently opened file.

If you press `F5` or switch to the *Debug* view in VS Code you will start one of these launch scripts. They are located in the file `.vscode/launch.json`. You may add as many scripts as you want.

If not already present, create the file `.vscode/launch.json` and add the following two launch configurations to this file:
```json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Jest All",
      "program": "${workspaceFolder}/node_modules/@angular/cli/bin/ng",
      "cwd": "${workspaceFolder}",
      "args": [
        "test",
        "--testMatch=\"**/+(*.)+(spec|test).+(ts|js)?(x)\"",
        "--runInBand"
      ],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen",
      "disableOptimisticBPs": true,
    },
    {
      "type": "node",
      "request": "launch",
      "name": "Jest Current File",
      "program": "${workspaceFolder}/node_modules/@angular/cli/bin/ng",
      "cwd": "${workspaceFolder}",
      "args": [
        "test",
        "--testMatch=\"**/+(*.)+(spec|test).+(ts|js)?(x)\"",
        "--testPathPattern=${fileBasenameNoExtension}",
        "--runInBand",
      ],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen",
      "disableOptimisticBPs": true,
    }
  ]
}

```

The first script `Jest All` will run all tests. The second one `Jest Current File` will run all specs with a path that matches the filename (without extension) of your currently opened file. This means if you have opened either `app.component.ts` or `app.component.spec.ts` it will run the test `app.component.spec.ts` because both `app.component` and `app.component.spec` match the path of your spec. Smart, isn't it? 😉

You can now set breakpoints in your specs or components that will be hit upon running these scripts. It gives you the ability to use the entire toolchain of Visual Studio Code's debugging features. You don't need to console-log all your variables, you can inspect them right inside your editor while stepping through the program execution. 🤯

![Animated GIF showing debugging features of Visual Studio Code](https://thepracticaldev.s3.amazonaws.com/i/i1ppgex66cj9sm5wnlqb.gif)

## (Un)known Issues
It really had some trouble with the launch configurations. Sometimes it worked. Most times it causes Jest not to find any tests. After about half a day of searching, debugging and trying out different parameters, I found out that this is caused by the default `testMatch` parameter. It appears that VS Code uses the `workspaceFolder` and injects it into the parameter. However, it appears that the underlying Regex is not evaluated.

The solution was to explicitly set the current working directory (`"cwd": "${workspaceFolder}"`) and overwriting the default `testMatch` parameter while removing the absolute path that VS Code injects itself (`"--testMatch=\"**/+(*.)+(spec|test).+(ts|js)?(x)\""`).

# Bright Future<a name="outlook"></a>
As mentioned in the beginning, I will probably look up my own blog post several times. At some point in future and if I have some spare time, I want to create my own [Angular Schematic](https://angular.io/guide/schematics) to `ng new` an Angular project and automate these initial steps. The schematic may then basically do the following:
1. `ng new` with parameter `--minimal` to prevent installing of any Karma or Jasmine related dependencies and files
2. Remove E2E mentions from the `README` and npm scripts for now (because they will still be added)
3. Add Jest dependencies
4. Add `tsconfig.spec.json` and adjust the `tsconfig.json`
5. Add `test` section to `angular.json`
6. Add `jest.config.js`
7. Add `test` and `test:watch` npm scripts
8. Add `launch.json`

I hope that you will also find some value in this article. If you have any questions or remarks, just let me know. Your feedback is very welcome!

You can find the sources for this guide on GitHub:
{% github https://github.com/devinmotion/angular-jest-vscode-debugging no-readme %}

### References<a name="references"></a>
*There are schematics like the one from [Briebug](https://github.com/briebug/jest-schematic) that add Jest with a single command. However, it does some things a bit different, I wanted to describe the steps to take without a schematic first and wanted to reduce the pain of setup debugging in VS Code.

- [Official Angular Documentation on Testing](https://angular.io/guide/testing)
- [Official Jest Documentation](https://jestjs.io/)
- [Angular in Depth - Angular CLI: “ng test” with Jest in 3 minutes (v2)](https://blog.angularindepth.com/angular-cli-ng-test-with-jest-in-3-minutes-v2-1060ddd7908d)
- [VSCode Recipies - Debugging Jest with VS Code](https://github.com/microsoft/vscode-recipes/tree/master/debugging-jest-tests)
- [Nrwl Blog - Debugging Nx in VS Code](https://blog.nrwl.io/debugging-nx-in-vs-code-cb1dbe9eeeba)
- [alejandro@Medium - “No tests found, exiting with code“ debug Jest test, VSCode paraWindows.](https://medium.com/@afdiaz_/no-tests-found-exiting-with-code-debug-jest-test-vscode-para-windows-73eded3f7cb7)