Table of Contents
In this tutorial we will learn how to install Jest and Enzyme, and create Snaphot tests.
If you are not familiar with these tools please read the links first since we will focus mainly on installing and using the tools.
The basic idea is that we are going to create a test cases, that will render the component, and create a snapshot of it. Next time when we run the test we could compare with the snapshot, making sure that either the component is not changed, or it renders as expected.
Setting up Jest and Enzyme.
We will need some extra packages in order to make Jest work with Enzyme.
Installing the packages.
- enzyme – the actual enzyme testing utility.
- enzyme-adapter-react-16 – the react adapter for enzyme. There are different adapters for different React versions, and since we are using react 16 we will install this version. A full list of adapter packages could be found here
- enzyme-to-json – a serializer for the components.
yarn add jest enzyme enzyme-adapter-react-16 enzyme-to-json --dev
In addition we have to also install:
yarn add babel-jest --dev
- babel-jest – allows Jest to use babel to transpile JS.
Before using Enzyme we have to configure it first. Create src/setupTests.js
file with the following contents:
src/setupTests.js
import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; configure({ adapter: new Adapter() });
This tells Enzyme, to run with React using the adaptor we just installed. Make sure this file is included before using Enzyme. Open package.json and add the following config:
./package.json
... }, "jest": { "setupFiles": [ "./src/setupTests.js" ] }, ...
Now we are almost ready to write our first test. Header component is perfect candidate since it is not connected to the redux store.
Create new file in the ./src/components/Header
folder and let’s
Add a Snapshot Test
./src/components/Header/index.test.js
import React from 'react'; import { shallow } from 'enzyme'; import Header from './index'; import toJson from 'enzyme-to-json'; describe('Testing Header component', () => { it('renders as expected!!!', () => { const wrapper = shallow( <Header title="Title test" /> ); expect(toJson(wrapper)).toMatchSnapshot(); }); });
unfortunately if we do yarn test
it will fail with error code, since Jest doesn’t know how to deal with anything different than Java Script.
But there is an easy fix: we could map CSS to a JS file that Jest understands. Create empty file in
./src/__mocks__/styleMock.js
and set up Jest to use the mapping:
./package.json
... "jest": { "setupFiles": [ "./src/setupTests.js" ], "moduleNameMapper": { "\\.(css|less|scss)$": "<rootDir>/src/__mocks__/styleMock.js" } }, ...
Now let’s run yarn test
and we should see something like this:
yarn test yarn run v1.12.3 $ jest PASS src/components/Header/index.test.js Testing Home component ✓ renders as expected!!! (15ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 1 passed, 1 total Time: 2.194s Ran all test suites. ✨ Done in 3.05s.
Perfect ! Our first snapshot test is done.
Testing renderer component
src/components/Home/renderer.test.js
import React from 'react'; import { shallow, mount } from 'enzyme'; import toJson from 'enzyme-to-json'; import Component from './renderer'; describe('Testing Home component', () => { it('renders as expected!!!', () => { const wrapper = shallow(<Component styles={{}} title="MyTest" />); expect(toJson(wrapper)).toMatchSnapshot(); expect(wrapper.contains(<div>MyTest</div>)).toBe(true); }); });
Also we added expect(wrapper.contains(<div>MyTest</div>)).toBe(true);
which looks for certain things into the component.
Testing Redux connected component.
./src/components/About/index.test.js
import React from 'react'; import { mount } from 'enzyme'; import toJson from 'enzyme-to-json'; import { Provider } from 'react-redux'; import reducers from '../../reducers'; import { createStore} from 'redux'; import About from './index.js'; const store = createStore(reducers, {}); let wrapper; describe('Testing About component', () => { beforeEach(() => { // Runs a function before each of the tests in this file runs wrapper = mount( <Provider store={store}> <About userName="Toni" /> </Provider> ); }); it('renders as expected', () => { expect(toJson(wrapper)).toMatchSnapshot(); }); it('expect to have state.userName set to John', () => { wrapper.setState({userName: "John"}); expect(wrapper.state('userName')).toEqual("John"); }); });
since we wrapped MyComponent with Provider component, we have to mount the component that will render it in full instead of doing shallow rendering (like in the previous example). Otherwise shallow will render only the Provider component.
Now the two tests could be executed together yarn test
PASS src/components/About/index.test.js Testing About component ✓ renders as expected (56ms) ✓ expect to have state.userName set to John (14ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 1 passed, 1 total Time: 3.102s Ran all test suites matching /.\/node_modules\/jest\/bin\/jest.js|src\/components\/About\/index.test.js/i.
Adding more tests.
./src/components/Greetings/renderer.test.js
import React from 'react'; import { mount } from 'enzyme'; import toJson from 'enzyme-to-json'; import reducers from '../../reducers'; import { createStore} from 'redux'; import Component from './renderer'; const store = createStore(reducers, {}); let wrapper; describe('Testing Greetings component', () => { beforeEach(() => { wrapper = mount( <Component styles={{}} store={store} /> ); }); it('renders as expected', () => { // to snapshot test expect(toJson(wrapper)).toMatchSnapshot(); }); it('textbox does not exist', () => { expect(wrapper.find('input').exists()).toBe(false); }); it('textbox exists after h2 click', () => { // simulate click on the h2 tag trigering props change and visualizing the input text box wrapper.find('h2').simulate('click'); expect(wrapper.find('input').exists()).toBe(true); }); it('textbox values change properly', () => { // text box value tests expect(wrapper.find('input').props().value).toBe('No Name'); wrapper.find('input').props().value = 'test'; expect(wrapper.find('input').props().value).toBe('test'); }); });
- beforeEach – Runs a function before each of the tests in this file runs. If the function returns a promise or is a generator, Jest waits for that promise to resolve before running the test.
- here is another way of passing the store directly to the component.