- type: FrontEnd
- title: Testing React components with Jest and Enzyme
- Link: hackernoon.com/testing-rea…
- author: Artem Sapegin
The introduction
Some people say that testing the React component isn’t very useful in general, but I think it’s necessary in a few scenarios:
- Component library,
- Open source projects,
- Integration with third-party components,
- Bugs, preventing recurrence.
I’ve tried a lot of tool combinations, but in the end, if I can recommend them to another developer, I’d rather recommend the following combination:
- Jest, a testing framework;
- Enzyme, React test library;
- Enzyme -to-json converts the snapshot that the enzyme wrapper type matches Jest
I often use shallow rendering and Jest snapshot testing in my tests.
Snapshot test in Jest
Shallow rendering
Shallow rendering refers to rendering a component as a virtual DOM object, but rendering only the first layer and not all child components. So even if you make a change to a subcomponent it won’t affect the output of the shallow render. Or a bug in an introduced child component will not affect the shallow rendering of the parent component. Shallow rendering is DOM independent.
Here’s an example:
const ButtonWithIcon = ({icon, children}) = > (
<button><Icon icon={icon} />{children}</button>
);
Copy the code
React will render as follows:
<button>
<i class="icon icon_coffee"></i>
Hello Jest!
</button>
Copy the code
But in shallow rendering it will only render as follows:
<button>
<Icon icon="coffee" />
Hello Jest!
</button>
Copy the code
Note that the Icon component is not rendered.
A snapshot of the test
A Jest snapshot is like a static UI with a combination of text characters representing Windows and buttons: it is the rendered output of a component stored in a text file.
You can tell Jest which components will output UI without unexpected changes, and Jest will save it to a file like this at runtime:
exports[`test should render a label 1`] = ` `;
exports[`test should render a small label 1`] = ` `;
Copy the code
Each time you make a change to a component, Jest compares the value of the current test and shows the difference, and asks you to update the snapshot if you make a change.
In addition to testing, Jest stores snapshots in files like __snapshots __ / label.spec.js.snap, which you need to submit at the same time.
Why Jest
- It’s very fast.
- Snapshot tests can be performed.
- Interactive monitoring mode tests only the parts that have been modified.
- The error message is very detailed.
- The configuration is simple.
- Supported by Mocks and Spies.
- You can generate test reports from the command line.
- The development prospect is very good.
- Don’t write error-prone assertions like ‘Expect (foo).to.be. A (‘ function’)’ because Jest only writes ‘expect(foo).to.be.true’ thus determines the correct assertion.
Why choose Enzyme
- Handy tool library package, can handle shallow rendering, static rendering markup and DOM rendering.
- Jquery-style API, easy to use and intuitive.
configuration
The first step is to install all dependencies including the same version dependencies:
npm install --save-dev jest react-test-renderer enzyme enzyme-adapter-react-16 enzyme-to-json
Copy the code
You also need to install the Babel plug-in babel-jest or TypeScript plug-in TS-jest
Update project package.json file:
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"jest": {
"setupFiles": ["./test/jestsetup.js"],
"snapshotSerializers": ["enzyme-to-json/serializer"]
}
Copy the code
Configuration item ‘snapshotSerializers’ allows you to transfer the enzyme package type to ‘Jest’ snapshot matches by using ‘enzyme-to-json’ without manual conversion.
Create a test/jestsetup.js file to customize the Jest runtime environment (setupFiles configuration item above)
import Enzyme, { shallow, render, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
// React 16 Enzyme adapter
Enzyme.configure({ adapter: new Adapter() });
// Make Enzyme functions available in all test files without importing
global.shallow = shallow;
global.render = render;
global.mount = mount;
Copy the code
You can also add the following configuration to package.json for CSS modules
"jest": {
"moduleNameMapper": {
"^.+\\.(css|scss)$": "identity-obj-proxy"
}
}
Copy the code
And run:
Install dependencies at the same time:
npm install --save-dev identity-obj-proxy
Copy the code
Mysql > Identity-obj-proxy Proxies can be played on/around/around/around/around/around/around/around/around/around/around/around/around
Test the rendering of the component
For most components that do not interact, the following test cases are sufficient:
test('render a label', () = > {const wrapper = shallow(
<Label>Hello Jest!</Label>
);
expect(wrapper).toMatchSnapshot();
});
test('render a small label', () = > {const wrapper = shallow(
<Label small>Hello Jest!</Label>
);
expect(wrapper).toMatchSnapshot();
});
test('render a grayish label', () = > {const wrapper = shallow(
<Label light>Hello Jest!</Label>
);
expect(wrapper).toMatchSnapshot();
});
Copy the code
Props test
Sometimes if you want to test more accurately and see real values. In that case you need to use Jest’s assertion in the Enzyme API.
test('render a document title', () = > {constwrapper = shallow( <DocumentTitle title="Events" /> ); expect(wrapper.prop('title')).toEqual('Events'); }); test('render a document title and a parent title', () => { const wrapper = shallow( <DocumentTitle title="Events" parent="Event Radar" /> ); Expect (wrapper. Prop (" title ")). ToEqual (' Events - the Event Radar '); });Copy the code
Sometimes you can’t use snapshots. For example, the component has a random ID like the following code:
test('render a popover with a random ID', () = > {const wrapper = shallow(
<Popover>Hello Jest!</Popover>
);
expect(wrapper.prop('id')).toMatch(/Popover\d+/);
});
Copy the code
Event test
You can simulate events like ‘click’ or ‘change’ and compare components to snapshots:
test('render Markdown in preview mode', () = > {const wrapper = shallow(
<MarkdownEditor value="*Hello* Jest!" />
);
expect(wrapper).toMatchSnapshot();
wrapper.find('[name="toggle-preview"]').simulate('click');
expect(wrapper).toMatchSnapshot();
});
Copy the code
Sometimes you want to test how an element in a child component affects the component. You need to use the Enzyme mount method to render a real DOM.
test('open a code editor', () = > {const wrapper = mount(
<Playground code={code} />
);
expect(wrapper.find('.ReactCodeMirror')).toHaveLength(0);
wrapper.find('button').simulate('click');
expect(wrapper.find('.ReactCodeMirror')).toHaveLength(1);
});
Copy the code
Test event handling
Similarly in event testing, instead of using the output rendering of the snapshot test component to test the event handler itself with the mock function of Jest:
test('pass a selected value to the onChange handler', () = > {const value = '2';
const onChange = jest.fn();
const wrapper = shallow(
<Select items={ITEMS} onChange={onChange} />
);
expect(wrapper).toMatchSnapshot();
wrapper.find('select').simulate('change', {
target: { value },
});
expect(onChange).toBeCalledWith(value);
});
Copy the code
It’s not just JSX
Jest uses JSON for snapshot testing, so you can test any function that returns JSON in the same way as the test component:
test('accept custom properties', () = > {const wrapper = shallow(
<Layout
flexBasis={0}
flexGrow={1}
flexShrink={1}
flexWrap="wrap"
justifyContent="flex-end"
alignContent="center"
alignItems="center"
/>
);
expect(wrapper.prop('style')).toMatchSnapshot();
});
Copy the code
Debugging and troubleshooting
Debug shallow renderer output
14. Use Enzyme debug method to print shallow renderer’s output:
const wrapper = shallow(/ * ~ * /);
console.log(wrapper.debug());
Copy the code
Enable failure tests for coverage
When your test fails, the diFF with the coverage flag looks like this:
-<Button
+<Component
Copy the code
Try replacing the arrow function component with a regular function component:
- export default const Button = ({ children }) = >{+export default function Button({ children }) {
Copy the code
RequestAnimationFrame error
When you run your tests, you may see the following error:
console.error node_modules/fbjs/lib/warning.js:42
Warning: React depends on requestAnimationFrame. Make sure that you load a polyfill in older browsers. http://fb.me/react-polyfills
Copy the code
React 16 relies on requestAnimationFrame, so you need to add a polyfill to your test code
// test/jestsetup.js
import 'raf/polyfill';
Copy the code
Reference source
- Jest cheat sheet
- Testing React Applications by Max Stoiber
- Migrating to Jest by Kent C. Dodds
- Migrating Ava to Jest by Jason Brown