Learning Jest and Enzyme

JestReactEnzyme

  We know that React is a JavaScript library created by Facebook for building UI components. React makes it painless to create interactive UIs. Design simple views for each state in your application.

   Today’s web applications are more complicated than ever. Libraries like React have opened up a whole new world of application complexity and functionality. In this article, I would like to share two popular JavaScript testing tools, Jest and Enzyme. Both Jest and Enzyme provide powerful tools for testing React UIs. Each is a unit testing tool that empowers you to ensure that the user interface of your web application looks and behaves as expected. Both are actively developed and easily installed using Node Package Manager.

     Jest and Enzyme are specifically designed to test React applications, Jest can be used with any other Javascript app but Enzyme only works with React. Jest can be used without Enzyme to render components and test with snapshots, Enzyme simply adds additional functionality. Enzyme can be used without Jest, however, Enzyme must be paired with another test runner if Jest is not used. Jest as the test runner, assertion library, and mocking library but Enzyme to provide additional testing utilities to interact with UI elements. So together Jest and Enzyme is a powerful testing tool for React applications.

More about Jest

      Jest is a JavaScript unit testing framework, used by Facebook to test services and React applications. As I mentioned Jest is a framework which has a task runner, assertion library, and good mocking support that means it could execute different unit test cases, write its result in console or log files, also could create mocks or verify all the assertions, in short, we can say it will execute the test. CRA (Create React App) comes bundled with Jest; it does not need to be installed separately. Another capability of Jest is that it also provides Snapshot testing, the ability to create a rendered ‘snapshot’ of a component, and compare it to a previously saved ‘snapshot’. The test will fail if the two do not match.

More about Enzyme

    Enzyme, created by Airbnb, adds some great additional utility methods for rendering a component (or multiple components), finding elements, and interacting with elements. As I mentioned Enzyme is a JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components output.  It must be installed in addition to tools already bundled with CRA. Enzyme on other hand is a library that wraps packages like React TestUtils, JSDOM and CheerIO to create a simpler interface for writing unit tests. Thus enzyme is not a test runner it doesn’t have its own assertion library as such it just provides a collection of API for unit testing. That’s why it could be integrated with Jest or any other task runner.

Setup Jest and Enzyme

     CRA (Create React App) comes bundled with Jest; it does not need to be installed separately. If you are not using CRA then use below command to install Jest,

npm install –save-dev jest

    You can simply install Enzyme via npm. You will need to install enzyme along with an Adapter corresponding to the version of React (or other UI Component library) you are using. For instance, if you are using enzyme with React 16, you can run below command,

npm install –save-dev enzyme enzyme-adapter-react-16 enzyme-to-json

The next step is to update your package.json file as below,

“jest”:
{
  “snapshotSerializers“: [“enzyme-to-json/serializer”]
}

  enzyme-to-json provides a better component format for snapshot comparison than Enzyme’s internal component representation. snapshotSerializers allows you to minimize code duplication when working with snapshots. Without the serializer each time a component is created in a test it must have the enzyme-to-json method .toJson() used individually before it can be passed to Jest’s snapshot matcher, with the serializer you never use it individually.

expect(toJson(rawRenderedComponent)).toMatchSnapshot();

   With this additional line in package.json it allows you to pass a component created by Enzyme to the Jest .toMatchSnapshot() without calling this interim JSON method.

Create a setupTests.js file at ./src/setupTests.js:

import { configure } from ‘enzyme’;
import Adapter from ‘enzyme-adapter-react-16’;
configure({ adapter: new Adapter() });

    CRA will automatically pick up this file, if not using CRA then also add this line in the same location as snapshotSerializers above:

“setupFiles”: [“./src/setupTests.js”],

Creating a test file

Jest will look for tests in any of the following places:

  • Files with .js suffix in __tests__ folders.
  • Files with .test.js suffix.
  • Files with .spec.js suffix.

    It is a convention to put each test file next to the code it is testing. This makes semantic sense, and also means relative paths are shorter ( ./MyComponent vs ../../MyComponent etc). Below is an example with a filename of MyComponent.test.js,

import React from ‘react’;
import { shallow } from ‘enzyme’;
import MyComponent from ‘./MyComponent’;
describe(‘MyComponent’, () => {
it(‘should render correctly in “debug” mode’, () => { const component = shallow(<MyComponent debug />);
expect(component).toMatchSnapshot();
});
});

   When npm test in CRA ran it will run all test files and output the results to the terminal. Customization flags exist to run against specific files only, or conversely ignore specific files using –– –testPathPattern filename/ and –– –testPathIgnorePatterns filename/.

Discussion about Mount, Shallow, Render

  Mount, Shallow, and Render are different types of methods to render your React components from your application.

import { mount, shallow, render } from ‘enzyme’;

     In order to have a component to test one of the above must be used, as in the example further above.

Mount

  • Full DOM rendering including child components.
  • Ideal for use cases where you have components that may interact with DOM API, or use React lifecycle methods in order to fully test the component.
  • As it actually mounts the component in the DOM .unmount() should be called after each test to stop tests affecting each other.
  • Allows access to both props directly passed into the root component (including default props) and props passed into child components.

Shallow

  • Renders only the single component, not including its children. This is useful to isolate the component for pure unit testing. It protects against changes or bugs in a child component altering the behavior or output of the component under test.
  • As of Enzyme 3 shallow components do have access to lifecycle methods by default.
  • Cannot access props passed into the root component (therefore also not default props), but can those passed into child components, and can test the effect of props passed into the root component. This is as with shallow(<MyComponent />), you’re testing what MyComponent renders – not the element you passed into shallow.

Render

  • Renders to static HTML, including children.
  • It does not have access to React lifecycle methods.
  • Less costly than mount but provides less functionality.

Below is an example of simple non-interactive components:

it(‘should render correctly with no props’, () => {
const component = shallow(<MyComponent/>);
expect(component).toMatchSnapshot();
});

it(‘should render banner text correctly with given strings’, () => {
const strings = [‘one’, ‘two’];
const component = shallow(<MyComponent list={strings} />);
expect(component).toMatchSnapshot();
});

     The Enzyme API has several ways to simulate events or user interactions. If you are wanting to test interacting with a child component then the mount method can be used. Below is an example:

it(‘should be possible to activate button with Spacebar’, () => {
const component = mount(<MyComponent />);
component.find(‘button#my-button-one’).simulate(‘keydown’, { keyCode: 32 });
expect(component).toMatchSnapshot();
component.unmount();
});

NOTE: components are like JavaScript functions. They accept arbitrary inputs (called props) and return React elements describing what should appear on the screen.

Mock Functions

    You may simply want to check that a function passed as props is successfully called. Below is an example to check the props successfully called,

const clickFn = jest.fn();
describe(‘MyComponent’, () => {
it(‘button click should hide component’, () => {
const component = shallow(<MyComponent onClick={clickFn} />);
component.find(‘button#my-button-two’).simulate(‘click’); expect(clickFn).toHaveBeenCalled();
});
});

   Getting more complex, you may wish to mock a function imported and used within MyComponent.js, set its return value, check it is called, and compare its snapshot.

     Lets imagine that within MyComponent.js we import { SaveToStorage } from ‘save-to-storage’ before creating a new SaveToStorage object, which has both TryGetValue and TrySetValue methods. TryGetValue has a default return value of false, if it returns true the component will change. Our component uses these within different button clicks. You can use jest.mock to mock this, as well as jest.fn to provide overrides for the functions within it.

const mockTryGetValue = jest.fn(() => false);
const mockTrySetValue = jest.fn();
jest.mock(‘save-to-storage’, () => ({
SaveToStorage: jest.fn().mockImplementation(() => ({
tryGetValue: mockTryGetValue,
trySetValue: mockTrySetValue,
})),
}));

describe(‘MyComponent’, () => {
it(‘should set storage on save button click’, () => {
mockTryGetValue.mockReturnValueOnce(true);
const component = mount(<MyComponent />);
component.find(‘button#my-button-three’).simulate(‘click’);
expect(mockTryGetValue).toHaveBeenCalled();
expect(component).toMatchSnapshot();
component.unmount();
});
});

Which one is the best among Shallow or Mount?

  • Recommended using shallow as much as possible because unit tests should be isolated as much as possible.
  • No needs to check the units (components) inside a unit (component)when running a single test.
  • When you use mount you are automatically exposed to the logic of all the units (components) in your render tree making it impossible to only test the component.
  • It is more costly in execution time when using the mount, because it requires JSDOM.

     I hope this article will give you a basic knowledge of JavaScript testing utility Jest and Enzyme. Try to utilize Jest and Enzyme together whenever you have an opportunity to test React components.

make it perfect!