Quick start to Jest unit testing

Type of test

  • unit testing
  • integration testing
  • End to end test (E2E)

Two key issues need to be addressed to test the React component

  • How to render components to be tested;
  • How to test the rendered components;

Shallow rendering

For component rendering, Airbnb, as a pioneer in heavily using React, has long proposed a special solution: Enzyme

$ npm install enzyme enzyme-adapter-react-16

An important function provided by Enzyme is Shallow Rendering of components. It allows us to render only the parent component without rendering all its child components when running tests. Shallow Rendering is very fast, so it is very suitable for unit testing.

// src/App.test.js
import React from 'react';
import { shallow } from 'enzyme';

import App from './App';

describe('app component', () => {
  it('contains a header with the "Hello world!"', () => {
    const app = shallow(<App />);
    expect(app.containsMatchingElement(<h1>Hello world!</h1>)).toEqual(true);
  });
});

For test rendered components, the Matcher provided by Jest is not easy to use. In fact, the community also offers a better choice - a Matcher library customized for Enzyme: Enzyme matchers. These matchers make writing assertion statements easier and more readable.

$ npm install jest-enzyme

It can be seen that in the first test case, we used toContainReact as a Matcher. Its meaning is very obvious and clear at a glance; In the following test cases, we use todoList.find('li ') to obtain the Li element array and judge whether its length meets the requirements.

// src/TodoList.test.js
import React from 'react';
import { shallow } from 'enzyme';

import ToDoList from './ToDoList';

describe('ToDoList component', () => {
  describe('when provided with an empty array of tasks', () => {
    it('contains an empty <ul> element', () => {
      const toDoList = shallow(<ToDoList tasks={[]} />);
      expect(toDoList).toContainReact(<ul />);
    });

    it('does not contain any <li> elements', () => {
      const toDoList = shallow(<ToDoList tasks={[]} />);
      expect(toDoList.find('li').length).toEqual(0);
    });
  });

  describe('when provided with an array of tasks', () => {
    it('contains a matching number of <li> elements', () => {
      const tasks = ['Wash the dishes', 'Make the bed'];
      const toDoList = shallow(<ToDoList tasks={tasks} />);
      expect(toDoList.find('li').length).toEqual(tasks.length);
    });
  });
});

Full rendering

In view of the limitations of shallow rendering, Enzyme provides the full rendering function mount. Modify the TodoList test file with the following code:

// src/TodoList.test.js
import React from 'react';
import { shallow, mount } from 'enzyme';

import ToDoList from './ToDoList';

describe('ToDoList component', () => {
  // ...

    it('contains a matching number of <li> elements', () => {
      const toDoListInstance = mount(<ToDoList tasks={tasks} />);

      toDoListInstance.find('Task').forEach((taskInstance) => {
        const taskProps = taskInstance.props();
        const matchingTask = tasks.find((task) => task.id === taskProps.id);
        const listItem = taskInstance.first('li');
        expect(listItem.text()).toBe(matchingTask.name);
      });
    });
  });
});

Because the mount function simulates the actual DOM, the rendering cost is higher, so it takes more time to run the test. We usually use the mount function in integration testing to test how components work together, not just as independent units.

Snapshot test

Snapshot testing is one of Jest's signature features. The so-called snapshot can be simply interpreted as a "code screenshot" of our application. When we run the snapshot test, Jest will render the component and create its snapshot file. This snapshot file contains the entire structure of the rendered component and should be submitted to the code base together with the test file itself. When we run the snapshot test again, Jest will compare the new snapshot with the old snapshot. If they are inconsistent, the test will fail, which helps us ensure that the user interface will not change unexpectedly.

Add a snapshot test to the test code of TodoList:

// src/TodoList.test.js
// ...

describe('ToDoList component', () => {
  // ...

  describe('when provided with an array of tasks', () => {
    // ...

    it('should render correctly', () => {
      const toDoListInstance = shallow(<ToDoList tasks={tasks} />);
      expect(toDoListInstance).toMatchSnapshot();
    });
  });
});

After running the above code, the ToDoList.test.js.snap file will be generated. Its contents are as follows:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ToDoList component when provided with an array of tasks should render correctly 1`] = `
<ul>
  <Task
    id={0}
    key="0"
    name="Wash the dishes"
  />
  <Task
    id={1}
    key="1"
    name="Make the bed"
  />
</ul>
`;

If we make any changes to the ToDoList component, the snapshot test will fail and show the exact difference between the current rendering result and the snapshot. If we want to update all failed snapshots, we can use the - u flag (alias -- updateSnapshot) to run Jest. Enter the following command to update all snapshots with one click:

$ npm test -- -u

reference resources

JavaScript test series (I): Test React components with Jest and Enzyme

JavaScript test series (II): deep rendering and snapshot test

Tags: Javascript git github React server

Posted on Wed, 17 Nov 2021 04:16:32 -0500 by basheer12m