DEV Community

Feralamillo
Feralamillo

Posted on • Originally published at Medium on

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

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.

running tests

Improvements

I’m always looking for feedback so please leave any suggestions or recommendations below.

Oldest comments (0)