Jest unit and snapshot testing with TypeScript in a next.js app
Since I was asked, I will also throw Jest for testing into the stack composed of next.js and TypeScript. So let's have a look into how that can be achieved.
Add Jest
First we need to add Jest:
npm i -D jest @types/jest ts-jest
Next we need to update our package.json for jest configs and to also add the run script:
// package.json
{
...
"scripts": {
...
"test": "jest",
}
...
"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"
}
}
}
As you can see in the jest config, we specified an additional tsconfig
file. So we create this one next:
// jest.tsconfig.json
{
"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"]
}
Running the test script with npm run test
lets us know, that jest cannot find any test. Makes sense, since we have not yet create one. So let us add a very simple unit test. First we create a function that we want to test:
// components/Button.tsx
import * as React from 'react'
export function giveMeFive(): number {
return 5
}
type Props = {
buttonText: string
}
export default (props: Props) => (
<button onClick={e => console.log(giveMeFive())}>{props.buttonText}</button>
)
We added a small function, that we also export so we can test it. Next we will add the jest test:
// components/__test__/button.tests.ts
import { giveMeFive } from '../Button'
test('generateAttributeIds', () => {
expect(giveMeFive()).toBe(5)
})
When running the test it succeeds and jest runs with TypeScript.
Snapshot testing with jest and TypeScript
At first more packages... Yay!
npm i -D react-test-renderer
Next we can add a snapshot test (note that we create a tsx file this time):
// components/__tests__/button.snapshot.test.tsx
import * as React from 'react'
import Button from '../Button'
import renderer from 'react-test-renderer'
it('renders correctly', () => {
const tree = renderer.create(<Button buttonText="Some Text" />).toJSON()
expect(tree).toMatchSnapshot()
})
Running the test shows again everything is successful. Since it was the first time we were running the snapshot test, a snapshot is created. You can find them within the newly created __snapshots__
folder.
Now let us change the Button component just a little bit. And pretend it was an accident:
import * as React from 'react'
export function giveMeFive(): number {
return 5
}
type Props = {
buttonText: string
}
export default (props: Props) => (
<button onClick={e => console.log(giveMeFive())}>{props.buttonText}!</button>
)
Do you see the difference? No? It is hard to find... But let us run the test once again:
Now we can see there is a !
that was added. Since we really like it, we can now update the snapshot:
npm test -- -u
And run the test again with npm run test
. We can see the snapshot was updated and the new testrun is successful again:
That is it for now, I hope it was useful :)