Pull Request that shows some examples using paragraphs.cordulack.com
Overview
Jest: Multi-featured test runner (it can run coverage reports, set up test doubles, run assertions)
Enzyme: Tests the output of your components
- https://enzymejs.github.io/enzyme/
- Cheatsheet: https://devhints.io/enzyme
- "Enzyme can be used within Jest to render components, to access the DOM of these components, and to make assertions based on the DOM" (Robin Wieruch, "How to test React with Jest & Enzyme", https://www.robinwieruch.de/react-testing-jest-enzyme", 2019.)
- "It seeks out components on the front end, interacts with them, and raises a flag if any of the components aren’t working the way it’s told they should." (Kingsley Silas, "Writing Tests for React Applications Using Jest and Enzyme", https://css-tricks.com/writing-tests-for-react-applications-using-jest-and-enzyme/, 2019)
Setting up Jest and Enzyme with Create React App
Jest should already be working after you set up your application using create-react-app
If you are using React 16 and set up your app using create-react-app
, you don't need a jest.config.js to point jest at your setupTests.js file. Create React App will find it automatically: https://create-react-app.dev/docs/running-tests#initializing-test-environment
setupTests.js (in your src directory)
// setup file
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
Import what you need from enzyme at the top of your test files
import { mount } from 'enzyme';
Troubleshooting
- I ran into this error when I first tried to use Enzyme's instructions for setting it up with Jest:
SyntaxError: Cannot use import statement outside a module
, while you can troubleshoot this, I found that runningnpm run test
instead ofjest
cleared up the problem. It looks likecreate-react-app
had already everything set up.
Jest: How do you organize tests effectively?
Mocha using describe()
to nest tests into groups. What is the convention in Jest?
"To create tests, add it() (or test()) blocks with the name of the test and its code. You may optionally wrap them in describe() blocks for logical grouping but this is neither required nor recommended." (Running Tests, Create React App Documentation, https://create-react-app.dev/docs/running-tests#writing-tests)
If you use it()
, you can easily exclude tests
https://create-react-app.dev/docs/running-tests/#focusing-and-excluding-tests
Skip: rename it()
to xit()
Just run one (or focus) a test: rename it()
to fit()
Jest: Assuming you can group tests, does Jest have anything like before, after, beforeEach, afterEach, for helping in setting up and tearing down?
Sure does: https://jestjs.io/docs/en/setup-teardown
Jest: Coverage Reports when using create-react-app
Output your test coverage to the terminal by running your tests with npm run test -- --coverage
https://create-react-app.dev/docs/running-tests#coverage-reporting
Jest: Snapshots
https://jestjs.io/docs/en/snapshot-testing Jest allows you to take a "snapshot" of a rendered component. You can compare the output your tests generate to the snapshot to see if anything changed in your UI that shouldn't have. The snap shot gets created the first time you run your test and subsequent tests compare the results to the snapshot.
"A typical snapshot test case for a mobile app renders a UI component, takes a snapshot, then compares it to a reference snapshot file stored alongside the test. The test will fail if the two snapshots do not match: either the change is unexpected, or the reference snapshot needs to be updated to the new version of the UI component." ("Snapshot Testing", Jest Docs, https://jestjs.io/docs/en/snapshot-testing.)
“One of the most challenging aspects of my project was keeping the input and output files for each test case in sync. Each little change in functionality could cause all the output files to change. With snapshot testing I do not need the output files, the snapshots are generated for free by Jest! I can simply inspect how Jest updates the snapshots rather than making the changes manually.” – Kyle Davis working on fjs. (Kyle Davis in "Jest 14.0: React Tree Snapshot Testing" https://jestjs.io/blog/2016/07/27/jest-14.html, July 27, 2016)
"The snapshot artifact should be committed alongside code changes, and reviewed as part of your code review process. Jest uses pretty-format to make snapshots human-readable during code review. On subsequent test runs Jest will compare the rendered output with the previous snapshot. If they match, the test will pass. If they don't match, either the test runner found a bug in your code (in this case, it's component) that should be fixed, or the implementation has changed and the snapshot needs to be updated." ("Snapshot Testing, Jest Docs, https://jestjs.io/docs/en/snapshot-testing)
Nice article about Snapshot Testing: https://benmccormick.org/2016/09/19/testing-with-jest-snapshots-first-impressions/ https://egghead.io/lessons/javascript-use-jest-s-snapshot-testing-feature
Enzyme: Shallow, Mount, and Render
shallow()
: will render a component without any of its child components
https://enzymejs.github.io/enzyme/docs/api/shallow.html
mount()
: render a component and all child components then put them into the DOM. Call .unmount() after your test to clean it up. This allows you to interact with your component in your tests via JS methods. (Kingsley Silas, "Writing Tests for React Applications Using Jest and Enzyme", https://css-tricks.com/writing-tests-for-react-applications-using-jest-and-enzyme/, 2019)
render()
: render a component and child components as HTML (doesn't put them into the DOM) (Kingsley Silas, "Writing Tests for React Applications Using Jest and Enzyme", https://css-tricks.com/writing-tests-for-react-applications-using-jest-and-enzyme/, 2019)
Enzyme: Testing Function Components & What To Do About React Hooks
It appears that if you use class-based React components, you can create an instance of your component using shallow()
and access class methods after calling instance()
.
"useEffect() and useLayoutEffect() don't get called in the React shallow renderer."
- Source: https://enzymejs.github.io/enzyme/#react-hooks-support
- https://github.com/facebook/react/issues/17321
- https://github.com/facebook/react/issues/15275
Explanation from Dan Abramov: Use mount()
and mock child components instead of using shallow()
.
"Generally we've been using shallow renderer less and less at Facebook because it encourages brittle tests that verify implementation detail. So components that relied on it a lot introduced a lot of refactoring friction, and didn't catch valuable regressions anyway for us. (Your experience might differ.)
The main value proposition of shallow rendering is that you mock out the inner components. We see the value in this, but in our experience doing this at the module system level gives you more leeway when refactoring. For example, you can always jest.mock('Button', () => 'button')
to replace a child Button component with a mock. We've mostly been happy with this approach and found it more flexible."
Enzyme: Simulating Events
https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/simulate.html
Enzyme: Debugging using console.log()
If you try to use console.log() to log some output from your wrapper, you will get something like this ReactWrapper {}
. Enzyme has a helper function for outputting the string you are probably after.
console.log(thingYouWantToSee.debug());
https://enzymejs.github.io/enzyme/docs/api/ShallowWrapper/debug.html