Integrating Jest Testing in a TypeScript React app without using Create React App
Pre-requisites
Install React and TypeScript
$ npm i react react-dom typescript
Install type definations:
$ npm i --save-dev @types/react @types/react-dom
Install Jest
Jest is a testing framework created by Facebook and ts-jest is a TypeScript preprocessor that allows you to use Jest in TypeScript.
$ npm i --save-dev jest ts-jest @types/jest
Install enzyme
Enzyme is a JavaScript Testing utility for React built by Airbnb. It makes it easy and intuitive to test your React Components by mimicking jQuery's API for DOM manipulation and traversal.
$ npm i --save-dev enzyme enzyme-adapter-react-16 @types/enzyme @types/enzyme-adapter-react-16
Install react-test-renderer
The react-test-renderer library enables you to render React components as JavaScript objects without the need of a DOM.
$ npm i --save-dev react-test-renderer @types/react-test-renderer
React with TypeScript Integration
Create a tsconfig.json file
In order to write TSX or TS files, you need to update the compiler options so that itβs supported in .tsx and .ts files. The tsconfig.json file is the main configuration file for TypeScript. The tsconfig.json file identifies the presence of TypeScript files in the project. Adding this file will give you some control on the compilation of TypeScript into JavaScript.
Create tsconfig.json:
$ touch tsconfig.json
tsconfig.json
{
"compileOnSave": false,
"compilerOptions": {
"jsx": "react",
"baseUrl": "src",
"declaration": false,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"sourceMap": true,
"target": "es6",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
}
}
Configure Jest Configs
By default, Jest is configured to listen to only JavaScript files. So we have to update the Jest Configs to test our TypeScript files.
Create jest.config.js:
$ touch jest.config.js
jest.config.js
module.exports = {
preset: 'ts-jest',
snapshotSerializers: ['enzyme-to-json/serializer'],
testEnvironment: 'node',
transform: {
'^.+\\.tsx?$': 'ts-jest'
},
testRegex: '/__tests__/.*\\.test.(ts|tsx)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
setupFiles: ['<rootDir>/app/react/__tests__/setupTests.ts'],
collectCoverage: true,
collectCoverageFrom: ['app/react/**/*.{ts,tsx}', '!app/react/__tests__/api/api-test-helpers.ts']
};
- transform: Compiles our TSX to JavScript using ts-jest preset
- testRegex: Regex to detect what test files to run.
- setupFiles: Path to our setupTests.ts file
- collectCoverage: Indicates whether the coverage information should be collected while executing the test.
- collectCoverageFrom: Indicates a set of files for which coverage information should be or NOT be collected.
!app/react/__tests__/api/api-test-helpers.ts'
indicates not to collect coverage information onapi-test-helpers.ts
file.
Create Test Setup File
The setup file configures and sets up our testing environment which will be executed immediately before running the test code itself. Here we register our Enzyme Adapter before each test.
Create setupTests.ts
$ touch setupTests.ts
setupTests.ts:
import enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
enzyme.configure({ adapter: new Adapter() });
Creating our Tests
Create a Components folder then add the following CheckBox TypeScript component (CheckBox.tsx):
import React, { Component } from 'react';
export interface CheckBoxProp {
labelOn: string;
labelOff: string;
}
class CheckBox extends Component<CheckBoxProp> {
state = {
isChecked: false,
};
onChange = () => {
this.setState({isChecked: !this.state.isChecked});
};
render() {
return (
<label>
<input
type="checkbox"
checked={this.state.isChecked}
onChange={this.onChange}
/>
{this.state.isChecked ? this.props.labelOn : this.props.labelOff}
</label>
);
}
}
export default CheckBox;
Create a test directory:
$ mkdir __tests__
Create our test for the CheckBox Component:
import * as React from 'react';
import Enzyme, { shallow, ShallowWrapper } from 'enzyme';
import { create, ReactTestRenderer } from 'react-test-renderer';
import CheckBox, { CheckBoxProp } from '../CheckBox/CheckBox';
let wrapper: ShallowWrapper<CheckBoxProp>;
let snapshot: ReactTestRenderer;
beforeEach(() => {
const checkbox = <CheckBox labelOn="On" labelOff="Off" />;
wrapper = shallow(checkbox);
snapshot = create(checkbox);
});
describe('<CheckBox />', () => {
test('it matches the snapshot', () => {
expect(snapshot.toJSON()).toMatchSnapshot();
});
it('it should toggle checkbox label after click event', () => {
expect(wrapper.text()).toEqual('Off');
wrapper.find('input').simulate('change');
expect(wrapper.text()).toEqual('On');
});
});
Update package.json with jest script:
"scripts": {
"test": "jest"
}
Run tests
$ npm run test