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.