Jest snapshot test snapshot

Snapshot testing is a useful tool whenever you want to make sure your UI doesn’t change unexpectedly.

// app.js
export function app() {
    return {
        server: 'http://localhost'.port: '8080',}}// Test the file
test('App Snapshot Test', () => {
    expect(app()).toMatchSnapshot();
})
Copy the code

After running the test case for the first time, a __snapshots__ folder is generated in the same directory with a snapshot file of the app.js file named app.test.js.snap

exports['App snapshot test 1'] = ` Object { "server": "http://localhost", "port": "8080", } `;
Copy the code

After running the test case, the generated snapshot is compared with the snapshot generated before. If the snapshot is consistent, the test passes. If the code is changed and the snapshot is inconsistent, the test fails. At this point, press u in the terminal to update the snapshot. Then the test passes and the snapshot is saved as the latest snapshot.

If we modify more than one snapshot at a time, multiple snapshot tests will not pass. Press u to update all snapshots. But if we want to go through the snapshots one by one and decide whether to update them, we need to update them one by one. At this time, we press the W key, there will be a variety of shortcut key modes, and the I key interactive update snapshots meet our needs.

{time: new Date()}

// app.js
export function app() {
    return {
        server: 'http://localhost'.port: '8080'.time: new Date(),}}// Test the file
test('App Snapshot Test', () => {
    expect(app()).toMatchSnapshot({
        time: expect.any(Date)), // Indicates that time can be any time, such as Date, String, or Number
    });
})
Copy the code

If you want to test a component snapshot, you are advised to use the react-test-renderer to better test the component snapshot.

npm i react-test-renderer -D
Copy the code
// Test the file
import renderer from 'react-test-renderer';
test('Snapshot Test', () = > {const tree = renderer.create(<App />);
    expect(tree.toJSON()).toMatchSnapshot();
})
Copy the code

Mock asynchronous functions

We’ve shown that you can test asynchronous requests using the Mock Axios module. Now we can test asynchronous requests using the Mock asynchronous request function.

A file that makes an asynchronous request

// api.js
export const fetchData = (a)= > {
    return axios.get('http://www.dell-lee.com/react/api/demo.json');
}
Copy the code

Create a folder __mocks__ in its parent directory, and then create the same file api.js

// __mocks__/api.js
export const fetchData = (a)= > {
    return new Promise((resolved, reject) = > {
        resolved({
            data: 123,})})}Copy the code

So in the test file, Jest will automatically go to the __mocks__ folder to find the mock api.js file to test:

import { fetchData } from './api.js';
jest.mock('./api.js');

test('Test return value 123', () = > {return fetchData().then(res= > {
        expect(res.data).toBe(123); })})Copy the code

For example, sometimes there is a function getNumber() in the api.js file that does not require a mock, and this function is introduced in the test file. Using the jest.requireActual() method, you pull the import function from the actual api.js file.

const { getNumber } = jest.requireActual('./api.js');
Copy the code

mock timers

Create a timer.js file and execute the callback function after 3 seconds:

// timer.js 
export default (callback) => {
    setTimeout((a)= > {
        callback();
    }, 3000)}// Test case
test('Test callback function', (done) => {
    timer((a)= > {
        expect(1).toBe(1); done(); })})Copy the code

The test case will wait 3 seconds before returning the test result. If the waiting time is too long, it is not conducive to our testing, so we need to add jest. UseFakeTimers () to help us simulate the timer and use jest.

// Test case
jest.useFakeTimers();
test('Test callback function', () = > {const fn = jest.fn(); Mock a callback function with jest
    timer(fn);
    jest.runAllTimers(); 
    expect(fn).toHaveBeenCalledTimes(1);
})
Copy the code
  • jest.runAllTimers(): Fast forward time so that all timer callbacks are executed at once
  • jest.runonlyPendingTimers(): Only the waiting timer is executed immediately
  • jest.advanceTimersByTime(1000): Fast forward to 1 second and execute the timer callback
// timer.js
export default (callback) => {
    setTimeout((a)= > {
        callback();
        setTimeout((a)= > {
            callback();
        }, 3000)},3000)}// Test case
jest.useFakeTimers();
test('Test callback function', () = > {const fn = jest.fn();
    timer(fn);
    jest.runAllTimers(); 
    expect(fn).toHaveBeenCalledTimes(2);
})

test('Test callback function', () = > {const fn = jest.fn();
    timer(fn);
    jest.runonlyPendingTimers(); 
    expect(fn).toHaveBeenCalledTimes(1);
})

test('Test callback function', () = > {const fn = jest.fn();
    timer(fn);
    jest.advanceTimersByTime(3000); 
    expect(fn).toHaveBeenCalledTimes(1);
    jest.advanceTimersByTime(3000); 
    expect(fn).toHaveBeenCalledTimes(2);
})
Copy the code

Configure and use enzymes

Enzyme is Airbnb’s open source React test tool library. It functions the secondary encapsulation of the official test tool library ReactTestUtils, provides a simple and powerful API, and has built-in Cheerio, which implements DOM processing in a jquery-style way, and has a very friendly development experience. It has gained popularity in the open source community and was officially featured by React.

Install the enzyme in the development environment

npm i --save-dev enzyme enzyme-adapter-react- 16
Copy the code

Use enzyme to introduce enzyme in the test file

import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({ adapter: new Adapter() });
test(' ', () => {.... })Copy the code

Enzyme provides three test methods, each of which contains multiple apis

  • shallow: light render, is for officialShallow RendererThe encapsulation. Rendering a component as a virtual DOM object renders only the first layer and the child components are not rendered, making it very efficient. The DOM environment is not required and can be usedjQueryTo access component information.
  • mountFull rendering, which loads a component rendering into a real DOM node to test DOM API interaction and component lifecycle. Jsdom was used to simulate the browser environment.
  • renderStatic rendering, which renders the React component as a static HTML string, parses the string using the Cheerio library and returns an instance of Cheerio that can be used to analyze the HTML structure of the component. Of the three methods,shallowandmountBecause we’re returning a DOM object, we can usesimulateInteractive simulation, whilerenderThe method does not. generalshallowMethod can be used if you need to determine subcomponentsrenderIf you need to test the life cycle of a componentmountMethods.

The use of jest + enzyme

// App.js
import React from 'react';
function App(){
    return (
        <div className='app' title='btn'>hello world</div>)}export default App;
Copy the code
// Test the file
import App from './App.js';
import Enzyme, { shallow, mount, render } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });

test('Test node', () = > {const wrapper = shallow(<App />);
    expect(wrapper.find('.app').length).toBe(1);
    expect(wrapper.find('.app').prop('title')).toBe('btn');
})
Copy the code

GitHub enzyme has some new matchers for jas-enzyme, which can be used more easily.

Install the jest – enzyme:

npm i jest-enzyme --save-dev
Copy the code
// package.json
"jest": {
  "setupFilesAfterEnv": ['./node_modules/jest-enzyme/lib/index.js'],}Copy the code

We can then retest the above case using the new matcher:

test('Test node', () = > {const wrapper = shallow(<App />); 
    expect(wrapper.find('.app')).toExist();
    expect(wrapper.find('.app')).toHaveProp('title', 'btn');
})
Copy the code

jest-enzymeNew matchers added in:

Some apis for Enzyme:

  • type()Returns the type of the current component
  • text(): Returns the text content of the current component
  • html(): Returns the HTML code form of the current component
  • props(): Returns all properties of the root component
  • prop(key): Returns the specified property of the root component
  • state([key]): Returns the status of the root component
  • setState(nextState): Sets the status of the root component
  • setProps(nextProps): Sets the properties of the root component
  • find(selector): Return element
  • debug(): Returns a string of HTML code for the current component
  • exists([selector]): Returns a Boolean value for whether the current component exists
  • simulate(event[, ...args]: Component interaction
test('Test input change event', () = > {const wrapper = shallow(<App />);
    const inputElem = wrapper.find('input');
    inputElem.simulate('change', {
        target: { value: 'hello world' },
    })
    expect(inputElem.prop('value')).toBe('hello world');
})
Copy the code