How I configure jest on a typescript Node.js application

Published on October 22, 2019

Here is some of the jest configuration I have for a Node.js application in typescript that builds on a CI system.

Installation

First, add some libraries! :)

ts-jest runs jest on typescript projects with type checking.

jest-junit outputs the test results in a format that reporting tools can use to show test results. This is useful for CI systems like CircleCI, AzureDevOps, Jenkins etc.

yarn add --dev ts-jest jest-junit

package.json scripts for Jest

in your package.json add some new settings. The first is a new script for running tests locally. The second one is for ci.

    ...
    "scripts": {
      "test": "npx jest --runInBand --detectOpenHandles --colors --verbose --reporters=default",
      "test:ci": "npx jest --ci --runInBand --silent --colors --coverage"
    }
    ...

If you accidentally leave an unresolved promise or unresolved async call while writing your code jest can get stuck. The --detectOpenHandles switch will have jest try to figure out what is preventing itself from stopping. It’s really helpful to have this when it’s needed.

--colors just adds some terminal colors!

--runInBand helps to speed up tests. You can test having this on or off. On Azure Devops this setting speeds up my tests.

If you add any debug statements to your tests they will be surpressed by default. To see them in the output you can add --verbose. I only use this on my local environment.

On CI we output the results to a file so we don’t need to see the detailed test results on the terminal or standard output. --silent tells jest to suppress the terminal test output.

Also on CI we want to have the test coverage published so we can view it later. So we ask jest to process this using the --coverage switch.

jest.config.js

The next file I add, in the root of my project is a jest.config.js file. Note it is .js. This non-typescript file can cause issues with typescript expecting all source files to be in the src/ directory so you may have to ignore this file in your tsconfig.json. This depends on your setup though, so if you don’t have a problem then don’t worry about it. If you get stuck let me know!

You can have ts-jest create the jest.config.js file for you by using

yarn ts-jest config:init

This file is where we tell jest that we want to use ts-jest, and that we’re running Node.js.

The preset loads configuration that tells jest to look for .ts files and a few other things. There are other presets for tsx if you need that. Check out the ts-jest documentation for more details. You can do all this configuration manually if you like too. The preset is easy if it works for your project.

The reporters are what jest uses to create output. default is just console (I think) and jest-junit has jest emit an .xml file containing all our test results in a well known, exportable format. CI systems can display this nicely.

The coverage threshold setting allows you to set what code coverage level passes. Here I set that we need to have 90% coverage for all code branches (e.g. if, for, property?.property). I also set that we need 50% of statements covered. Even if all test pass, if the coverage thresholds fail then the tests will be marked as failing.

module.exports = {
  cacheDirectory: __dirname + '/.jest_cache',
  roots: ['<rootDir>/src'],
  preset: 'ts-jest',
  testRegex: '(.*.(test|spec)).(jsx?|tsx?|ts?)$',
  moduleFileExtensions: ['ts', 'js', 'json'],
  setupFiles: ['./src/testing/preRun.ts'],
  collectCoverage: true,
  collectCoverageFrom: [
    '**/src/**/*.ts',
    '!**/node_modules/**',
    '!**/*.test.data.ts',
  ],
  coverageThreshold: {
    global: {
      statements: 50,
      branches: 90,
      functions: 0,
      lines: 0,
    },
  },
  reporters: [
    'default',
    [
      'jest-junit',
      {
        outputName: 'junit-TEST.xml',
      },
    ],
  ],
  coveragePathIgnorePatterns: [
    '.*test\\.data\\.ts$,migrations.*.ts$,(.*.(test|spec)).(jsx?|tsx?)$,(tests/.*.mock).(jsx?|tsx?)$',
  ],
  coverageReporters: ['json', 'lcov', 'text', 'clover', 'cobertura'],
  verbose: true,
}

The setup file is also specified above. Jest will load this file before running all of your tests so it’s the place to set up libraries and imports that you need for your tests to run successfully. For example if you needed to bootstrap a dependency injection root, set a timezone or initialise reflect-metadata for typescript.

You may not need one of these. See the details about mine in the section below this one👇.

preRun.ts

Here I have the setup file we referenced in jest.config.js

I have two reasons to use this file.

The first is because I use the winston logger and I don’t want it emitting logs while running my tests. They just clutter the results. You may want to turn this on or off from time to time but this is the way I like it.

The second reason is the graphql integrations I use need to use reflect-metadata at runtime. So I load it here before anything else. If you don’t need either of these things you may not need this file.

import 'reflect-metadata'
import * as winston from 'winston'

// don't log while running tests
winston.remove(winston.transports.Console)
winston.remove(winston.transports.File)

And that’s it! You should be able to add your *.spec.ts files with tests now and jest will pick them up and run them when you use yarn test locally or yarn test:ci on your CI platform.

Darragh ORiordan

Hi! I'm Darragh ORiordan.

I live and work in Sydney, Australia building and supporting happy teams that create high quality software for the web.

I also make tools for busy developers! Do you have a new M1 Mac to setup? Have you ever spent a week getting your dev environment just right?

My Universal DevShell tooling will save you 30+ hours of configuring your Windows or Mac dev environment with all the best, modern shell and dev tools.

Get DevShell here: ✨ https://usemiller.dev/dev-shell


Read more articles like this one...

List of article summaries

#typescript

How to write an ESLint plugin in TypeScript

I use NestJS at my day job. It’s a complicated framework sometimes and there are lots of things that devs “just have to remember” or there will be bugs in your application that you won’t see until runtime.

I wanted to remove this cognitive load from NestJS engineers so that they can focus on valuable work instead. I wrote an ESLint plugin to alert developers directly in their IDE or editor when these common issues exist - (Available on NPM) https://www.npmjs.com/package/@darraghor/eslint-plugin-nestjs-typed

Here is what I learned about writing ESLint plugins in typescript for typescript while building the plugin.

#typescript

Create an only ever true boolean type in typescript

Integrating with an API where a boolean can only be undefined or true I needed to type it. It’s easy enough but I just wanted to jot it down in case any one else needs this in the future.

#typescript

How to change the type of output for a property filter in typescript

If you have an iterable filter on a property in typescript you might have a slightly different model coming out of the filter based on the filter condition.

By setting the types correctly you can potentially save having to add manual typing hints in whatever follows the filter.

It’s easier to show this with code so see below!

Thanks to one of my colleagues for suggesting this one in a code review.

#typescript

Copying missing files during a typescript build on deploy

If you build your nodeJS project using typescript, it’s important to know that the typescript compiler tsc will only include files that are .js or .ts.

If you have an issue where you deploy or build a typescript project and you get a file not found issue or similar, it’s probably because you missed that typescript does not process these kinds of files for you.

If you need to include files like XML (.xml) or images in your typescript project build output, you will have to copy them manually before or after the build process.

This is one way to copy assets in a typescript build.

Comments