Create react app typescript: testing with jest and enzyme
Feralamillo
Posted on February 28, 2020
Get your unit testing configuration ready in less than 10 minutes. In this article, you can find how to get jest and enzyme ready for your tests and Istanbul to collect the coverage.
Pre-requisite
As a first step, I’m going to install create react app with the typescript template.
npx create-react-app my-project --template typescript
It will take some time installing. I have included two versions, one for non-ejected versions of create-react-app and one for the ejected versions. I think the best is to avoid ejecting create-react-app as all the webpack config is taken care of. So, if you haven’t ejected, don’t do it!
Step 1: Install dependencies
npm i -D ts-jest jest-fetch-mock enzyme enzyme-adapter-react-16 enzyme-to-json [@types/enzyme](http://twitter.com/types/enzyme) [@types/enzyme-adapter-react-16](http://twitter.com/types/enzyme-adapter-react-16) --save-exact
Step 2: Include config files
Without ejecting
package.json
"jest": {
"collectCoverageFrom": [
"src/\*\*/\*.{js,jsx,ts,tsx}",
"!src/\*\*/\*.d.ts",
"!src/index.tsx",
"!src/serviceWorker.ts"
],
"coveragePathIgnorePatterns": [
"./src/\*/\*.types.{ts,tsx}",
"./src/index.tsx",
"./src/serviceWorker.ts"
],
"coverageReporters": [
"json",
"lcov",
"text-summary",
"clover"
],
"coverageThreshold": {
"global": {
"statements": 95,
"branches": 95,
"lines": 95,
"functions": 95
}
},
"snapshotSerializers": [
"enzyme-to-json/serializer"
],
"transform": {
"^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node\_modules/ts-jest"
},
"transformIgnorePatterns": [
"[/\\\\]node\_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
"^.+\\.module\\.(css|sass|scss)$"
],
"moduleNameMapper": {
"^react-native$": "react-native-web",
"^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy",
"src/(.\*)$": "<rootDir>/src/$1"
}
}
src/setupTests.ts
import Enzyme from 'enzyme';
import ReactSixteenAdapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new ReactSixteenAdapter() });
Ejected version
In case you decide to eject create-react-app
config/jest/setupEnzyme.ts
import Enzyme from 'enzyme';
import ReactSixteenAdapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new ReactSixteenAdapter() });
config/jest/setupJest.ts
import { GlobalWithFetchMock } from 'jest-fetch-mock';
const customGlobal: GlobalWithFetchMock = global as GlobalWithFetchMock;
customGlobal.fetch = require('jest-fetch-mock');
customGlobal.fetchMock = customGlobal.fetch;
config/jest/typescriptTransform.js
// Copyright 2004-present Facebook. All Rights Reserved.
'use strict';
const tsJestPreprocessor = require('ts-jest/preprocessor');
module.exports = tsJestPreprocessor;
jest.config.js
module.exports = {
roots: ['<rootDir>/src'],
preset: 'ts-jest',
globals: {
'ts-jest': {
diagnostics: true,
},
},
collectCoverageFrom: [
'src/\*\*/\*.{js,jsx,ts,tsx}',
'!src/\*\*/\*.d.ts',
'!src/serviceWorker.ts',
'!src/setupTests.ts',
'!src/index.tsx',
],
setupFiles: ['./config/jest/setupJest.ts', './config/jest/setupEnzyme.ts'],
coveragePathIgnorePatterns: ['./src/\*/\*.types.{ts,tsx}'],
coverageReporters: ['json', 'lcov', 'text-summary', 'clover'],
coverageThreshold: {
global: {
statements: 95,
branches: 95,
lines: 95,
functions: 95,
},
},
snapshotSerializers: ['enzyme-to-json/serializer'],
testMatch: ['<rootDir>/src/\*\*/\*.test.{js,jsx,ts,tsx}'],
automock: false,
transform: {
'^.+\\.(js|jsx|ts|tsx)$': '<rootDir>/node\_modules/ts-jest',
'^.+\\.css$': '<rootDir>/config/jest/cssTransform.js',
'^(?!.\*\\.(js|jsx|ts|tsx|css|json)$)':
'<rootDir>/config/jest/fileTransform.js',
},
transform: {
'^.+\\.(js|jsx|ts|tsx)$': '<rootDir>/node\_modules/ts-jest',
'^.+\\.css$': '<rootDir>/config/jest/cssTransform.js',
'^(?!.\*\\.(js|jsx|ts|tsx|css|json)$)':
'<rootDir>/config/jest/fileTransform.js',
},
modulePaths: [],
moduleNameMapper: {
'^react-native$': 'react-native-web',
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
'src/(.\*)$': '<rootDir>/src/$1',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
};
Step 3: scripts
Without ejecting
package.json
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
**"test:coverage": "react-scripts test --coverage --runInBand --watchAll=false",**
"eject": "react-scripts eject",
"lint": "eslint --ext .js,.jsx,.ts,.tsx src --color",
"format": "prettier --write src/\*\*/\*.{ts,tsx,scss,css,json}",
"isready": "npm run format && npm run lint && **npm run test:coverage** && npm run build"
},
Ejected version
package.json
"scripts": {
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"test": "node scripts/test.js",
**"test:coverage": "node scripts/test.js --env=jsdom --coverage --runInBand --watchAll=false",**
"lint": "eslint --ext .js,.jsx,.ts,.tsx src --color",
"format": "prettier --write src/\*\*/\*.{ts,tsx,scss,css,json}",
"isready": "npm run format && npm run lint && **npm run test:coverage** && yarn build"
},
Step 4: Creating first tests
As we already have the component with a test let’s just update that one. I prefer to always create a folder called tests to separate them from the components.
tests/App.test.tsx
import React from 'react';
import { shallow } from 'enzyme';
import App from '../App';
test('renders the component', () => {
const component = shallow(<App />);
expect(component).toMatchSnapshot();
});
When running npm run test a new snapshot will be created and there will be a new folder generated __snapshots__ with it.
Improvements
I’m always looking for feedback so please leave any suggestions or recommendations below.
Posted on February 28, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.