<<

Nailing Up a Typescript and Jest Project

This is a minimum viable setup for a Typescript project to enable testing with the Jest framework. Like anything Javascript related, it’s not that obvious and documentation is limited - so this is an outboard brain version as a reference.

Steps 1 through 4 are the important parts; 5 and 6 are just examples of tests once everything is up and running.

1. Create a new directory and initialise the project:

mkdir my-new-project

cd my-new-project

npm init

2. Install the dependencies:

npm install --save-dev typescript jest ts-jest @types/jest @types/node

Expect a long pause while the Internet is downloaded and installed.

3. Create the Jest config file

touch jest.tsconfig.json

Populate the file with this snippet:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "esnext",
    "jsx": "react",
    "sourceMap": false,
    "experimentalDecorators": true,
    "noImplicitUseStrict": true,
    "removeComments": true,
    "moduleResolution": "node",
    "lib": [
      "es2017",
      "dom"
    ],
    "typeRoots": [
      "node_modules/@types",
      "src/@types"
    ]
  },
  "exclude": [
    "node_modules",
    "out",
    ".next"
  ],
  "preset": "ts-jest"
}

4. Update the project config with Jest settings

In the package.json file, update the scripts key from

"scripts": {
   "test": "echo \"Error: no test specified\" && exit 1"
 },

to

"scripts": {
   "test": "jest"
 },

This will allow tests to be run with the npm test command.

Add the jest key to the end of the package.json file:

"jest": {
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js"
    ],
    "transform": {
      "^.+\\.tsx?$": "ts-jest"
    },
    "testMatch": [
      "**/*.(test|spec).(ts|tsx)"
    ],
    "globals": {
      "ts-jest": {
        "babelConfig": true,
        "tsconfig": "jest.tsconfig.json"
      }
    },
    "coveragePathIgnorePatterns": [
      "/node_modules/"
    ],
    "coverageReporters": [
      "json",
      "lcov",
      "text",
      "text-summary"
    ],
    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/mocks.js",
      "\\.(css|less)$": "<rootDir>/__mocks__/mocks.js"
    }
  }

The important things to note here are that any Typescript test files with a .ts suffix will be run through the ts-jest preprocessor; and that Jest will look for any files with a *.spec or *.test name pattern, and a *.ts or *.tsx file type.

The convention seems to be to locate test specs in a __tests__ subdirectory, but this testMatch setting will find them anywhere.

5. Write a failing test

Create a test file in the src/__tests__ directory:

mkdir -p src/__tests__

touch src/__tests__/main.spec.ts

Add a failing test to the main.spec.ts file:

import { isFoo } from '../main'

test('should return true given foo', () => {
  expect(isFoo('foo')).toBe(true)
})

Run the test, which will fail:

npm test

 FAIL  src/__tests__/main.spec.ts
  ● Test suite failed to run

    Cannot find module '../main' from 'src/__tests__/main.spec.ts'

6. Fix the failing test

Create a src/main.ts file:

touch src/main.ts

Add the function:

const isFoo = (testString: string) => {
    if (testString === "foo") {
        return true
    }
    return false
}

export { isFoo };

Rerun the tests: npm test

PASS  src/__tests__/main.spec.ts
 ✓ should return true given foo (3ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.948s
Ran all test suites.

At this point, you’ve got a working test harness and can iterate from there to start building out the actual application.

For the true test-driven development afficiando, this kind of “fix the failing test by writing the entire codebase” approach is a bit of antipattern - they’d start by creating an empty function to resolve the actual first error, then iterate from there - but that’s a subject for another post.