Jest and Enzyme
- Jest and Enzyme are both designed to test React applications; however, Jest can be used with any Javascript app, whereas Enzyme is only for React.
- Jest can render components and test with snapshots without Enzyme; Enzyme simply adds more functionality.
- Enzyme can be used without Jest, but if Jest is not used, Enzyme must be paired with another test runner.
Installing and configuring
If not using CRA install Jest:
npm install --save-dev jest babel-jest
Install Enzyme:
npm install --save-dev enzyme enzyme-adapter-react-16 enzyme-to-json
Update your package.json :
"jest": {
"snapshotSerializers": ["enzyme-to-json/serializer"]
}
In comparison to Enzyme's internal component representation, enzyme-to-json provides a better component format for snapshot comparison. When working with snapshots, you can use snapshotSerializers to reduce code duplication. Without the serializer, the enzyme-to-json method must be used every time a component is created in a test. The serializer never uses toJson() because it must be used individually before it can be passed to Jest's snapshot matcher.
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() });
If you don't want to use CRA, add this line in the same location as snapshotSerializers above:
"setupFiles": ["./src/setupTests.js"],
Creating a Test File
Jest will search for tests in the following locations:
- __tests__ folders contain files with the.js suffix.
- .test.js files are those that have a.test.js suffix.
- .spec.js files are those that have a.spec.js suffix.
Each test file should be placed next to the code it is testing. This makes sense in terms of semantics, and it also means that relative paths are shorter (./MyComponent vs../../MyComponent, and so on).
This file, MyComponent.test.js, is an example of this:
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 you run npm test in CRA, it will run all of the test files and print the results to the terminal. Using the -- --testPathPattern filename/ and -- --testPathIgnorePatterns filename/customization flags, you can run against specific files only or ignore specific files.
Mount, Shallow, Render
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.
Mounting:
- Rendering of the entire DOM, including child components.
- Ideal for use cases where you need to fully test a component that interacts with the DOM API or uses React lifecycle methods.
- To prevent tests from interfering with one another, unmount() should be called after each test.
- Allows access to props passed directly to the root component (including default props) as well as props passed to child components.
Shallow:
- Only the single component is rendered, not its children. This is useful for separating the component from the rest of the system during unit testing. It guards against a child component's changes or bugs affecting the behaviour or output of the component under test.
- Shallow components have access to lifecycle methods by default as of Enzyme 3.
- Cannot access props passed into the root component (and thus default props), but can test the effect of props passed into the root component. You're testing what MyComponent renders, not the element you passed into shallow, as with shallow(<MyComponent />).
Render:
- Including children, renders to static HTML.
- React lifecycle methods are not accessible.
- It's less expensive than a mount, but it's less functional.
Testing
Basic Component Rendering
For non-interactive, simple 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();
});
Events
There are several ways to simulate events or user interactions using the Enzyme API. If you want to test interacting with a child component, you can use the mount method.
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();
});
Mock Functions
You might just want to double-check that a function passed as props was called successfully.
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();
});
});
You might want to mock a function imported and used within MyComponent.js, set its return value, check that it is called, and compare its snapshot as things get more complicated.
Let's say we import {SaveToStorage} from'save-to-storage' before creating a new SaveToStorage object with both TryGetValue and TrySetValue methods in MyComponent.js. The default return value of TryGetValue is false; if it returns true, the component will change. These are used by our component in various button clicks.
We can mock this with jest.mock, and we can provide overrides for the functions within it with jest.fn.
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();
});
});
FAQs
-
What is the full form of CRA?
The full form of CRA is Create React App and it is a command that can be used to create react app like so:
npx create-react-app my-app
-
What does enzyme-to-json do?
In comparison to Enzyme's internal component representation, enzyme-to-json provides a better component format for snapshot comparison.
-
What are snapshotSerializers used for?
When working with snapshots, you can use snapshotSerializers to reduce code duplication.
-
Is Jest and React testing library same?
Jest combines fast iteration with powerful features such as mocking modules and timers to give you more control over how your code runs. The React Testing Library is a collection of helpers that enable you to test React components without having to worry about their implementation details.
-
What is Jest spyOn?
jest. spyOn allows you to mock the entire module or individual functions within it. It can be used to track method calls in its most basic form: Note that the above example is a condensed/modified version of the official Jest documentation.
Key Takeaways
In this article, we have extensively discussed using Enzyme with Jest.
We hope that this blog has helped you enhance your knowledge regarding Enzyme with Jest and if you would like to learn more, check out our articles on Coding Ninjas Studio. Do upvote our blog to help other ninjas grow. Happy Coding!