Create react app typescript: testing with jest and enzyme

Istanbul coverage report

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

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 @types/enzyme-adapter-react-16 --save-exact

Step 2: Include config files

Without ejecting

"jest": {
"collectCoverageFrom": [
"src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts",
"!src/index.tsx",
"!src/serviceWorker.ts",
"!src/reportWebVitals.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

/* eslint-disable import/no-extraneous-dependencies */
import Enzyme from 'enzyme';
import ReactSixteenAdapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new ReactSixteenAdapter() });

Ejected version

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

  "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

  "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

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.

running tests

Improvements

Fintech & Blockchain RocknRolla! If you want to go quickly, go alone. If you want to go far, go together. #entrepreneurship #fintech #blockchain

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store