|
| 1 | +--- |
| 2 | +id: migrate |
| 3 | +title: Migrate from Enzyme |
| 4 | +sidebar_label: Migrate from Enzyme |
| 5 | +--- |
| 6 | + |
| 7 | +This page does not go into detail, but it's for those who have experience in |
| 8 | +working with Enzyme and are trying to understand how to move to React Testing |
| 9 | +Library. It also has some helpful information for those who are comparing Enzyme |
| 10 | +with React Testing Library. |
| 11 | + |
| 12 | +## What is React Testing Library? |
| 13 | + |
| 14 | +React Testing Library is part of an open-source project named |
| 15 | +[Testing Library](https://github.com/testing-library). There are several other |
| 16 | +helpful tools and libraries in the Testing Library project which you can use |
| 17 | +them to write more useful tests. Beside React Testing Library, here are some |
| 18 | +other Testing Library's libraries that can help you along the way: |
| 19 | + |
| 20 | +- **[@testing-library/jest-dom](https://github.com/testing-library/jest-dom)**: |
| 21 | + jest-dom provides a set of custom jest matchers that you can use to extend |
| 22 | + jest. These will make your tests more declarative, clear to read, and to |
| 23 | + maintain. |
| 24 | + |
| 25 | +- **[@testing-library/user-event](https://github.com/testing-library/user-event):** |
| 26 | + user-event tries to simulate the real events that would happen in the browser |
| 27 | + as the user interacts with it. For example, userEvent.click(checkbox) would |
| 28 | + change the state of the checkbox. |
| 29 | + |
| 30 | +## Why should I use the React Testing Library? |
| 31 | + |
| 32 | +Enzyme is a good test library. The library and its contributors did so much for |
| 33 | +the community. Many of the React Testing Library maintainers used and |
| 34 | +contributed to Enzyme for years before developing and working on React Testing |
| 35 | +Library. So we want to say, thank you to the contributors of Enzyme! |
| 36 | + |
| 37 | +The primary purpose of the React Testing Library is to give you confidence by |
| 38 | +testing your components in the way the user will use them. Users don't care what |
| 39 | +happens behind the scenes. They just see and interact with the output. So, |
| 40 | +instead of accessing the components' internal API, or evaluating the `state`, |
| 41 | +you'll get more confidence by writing your tests based on the component output. |
| 42 | + |
| 43 | +React Testing Library aims to solve the problem that many developers face when |
| 44 | +writing tests with Enzyme which allows (and encourages) developers to test |
| 45 | +[implementation details](https://kentcdodds.com/blog/testing-implementation-details). |
| 46 | +Tests which do this ultimately prevent you from modifying and refactoring the |
| 47 | +component without changing the test. As a result, the tests slowed down your |
| 48 | +development speed and productivity, since every small change requires rewriting |
| 49 | +some part of your tests, even if the change you made does not affect the |
| 50 | +component's output. |
| 51 | + |
| 52 | +Re-writing your tests in React Testing library worthwhile because you're trading |
| 53 | +tests that slow you down for tests that give you more confidence and increase |
| 54 | +your productivity in the long run. |
| 55 | + |
| 56 | +## How to migrate from Enzyme to React Testing Library? |
| 57 | + |
| 58 | +One of the keys to a successful migrate is to do it incrementally, by running |
| 59 | +the two test libraries side by side in the same application and porting Enzyme's |
| 60 | +tests to React Testing Library one by one. It makes it possible to migrate even |
| 61 | +large and complex applications without disrupting other businesses because the |
| 62 | +work can be done collaboratively and spread over some time. |
| 63 | + |
| 64 | +## Install React Testing Library |
| 65 | + |
| 66 | +You can check |
| 67 | +[this page](https://testing-library.com/docs/react-testing-library/setup) for |
| 68 | +the complete installation and setup guide. |
| 69 | + |
| 70 | +Here is what you should do, first you need to install the React Testing Library: |
| 71 | + |
| 72 | +``` |
| 73 | +npm install --save-dev @testing-library/react @testing-library/jest-dom |
| 74 | +``` |
| 75 | + |
| 76 | +## Import React Testing Library to your test |
| 77 | + |
| 78 | +Let's say we're using Jest (you can use other test frameworks as well), then you |
| 79 | +just have to import the following modules into your test file: |
| 80 | + |
| 81 | +```jsx |
| 82 | +// import React so you can use JSX (React.createElement) in your test |
| 83 | +import React from 'react' |
| 84 | + |
| 85 | +/** |
| 86 | + * render: lets us render the component (like how React would) |
| 87 | + * screen: Your utility for finding elements the same way the user does |
| 88 | + **/ |
| 89 | +import { render, screen } from '@testing-library/react' |
| 90 | +``` |
| 91 | + |
| 92 | +The test structure is as same as how you would write it in Enzyme |
| 93 | + |
| 94 | +```jsx |
| 95 | +test('test title', () => { |
| 96 | + // Your tests come here... |
| 97 | +}) |
| 98 | +``` |
| 99 | + |
| 100 | +> Note: you can also use `describe` and `it` blocks with React Testing Library. |
| 101 | +> React Testing Library doesn't replace Jest, just Enzyme. We recommend `test` |
| 102 | +> because it helps with this: |
| 103 | +> [Avoid Nesting When You're Testing](https://kentcdodds.com/blog/avoid-nesting-when-youre-testing) |
| 104 | +
|
| 105 | +## Basic Enzyme to React Testing Library migration examples |
| 106 | + |
| 107 | +One thing to keep in mind that there's not a one-to-one mapping of Enzyme |
| 108 | +features to React Testing Library features. Many Enzyme features result in |
| 109 | +inefficient tests. Unfortunately, that means many of the features you're |
| 110 | +accustomed to with Enzyme will need to be left behind with Enzyme (no more need |
| 111 | +for a `wrapper` variable or `wrapper.update()` calls, etc.). |
| 112 | + |
| 113 | +React Testing Library has helpful queries which lets you access your component's |
| 114 | +elements and their properties, and here we'll show typical Enzyme tests with |
| 115 | +React Testing Library's alternatives. |
| 116 | + |
| 117 | +Let's say we have a `Welcome` component, which just shows a welcome message, and |
| 118 | +we will have a look at both Enzyme and React Testing Library tests to learn how |
| 119 | +we can test this component: |
| 120 | + |
| 121 | +**React Component** |
| 122 | + |
| 123 | +The following component gets a `name` from `props` and shows a welcome message |
| 124 | +in an `h1` element, it also has a text input which users can change to a |
| 125 | +different name, and the template updates accordingly. Check the live version on |
| 126 | +[Codesandbox](https://codesandbox.io/s/ecstatic-hellman-fh7in) |
| 127 | + |
| 128 | +```jsx |
| 129 | +const Welcome = props => { |
| 130 | + const [values, setValues] = useState({ |
| 131 | + firstName: props.firstName, |
| 132 | + lastName: props.lastName, |
| 133 | + }) |
| 134 | + |
| 135 | + const handleChange = event => { |
| 136 | + setValues({ ...values, [event.target.name]: event.target.value }) |
| 137 | + } |
| 138 | + |
| 139 | + return ( |
| 140 | + <div> |
| 141 | + <h1> |
| 142 | + Welcome, {values.firstName} {values.lastName} |
| 143 | + </h1> |
| 144 | + |
| 145 | + <form name="userName"> |
| 146 | + <label> |
| 147 | + First Name |
| 148 | + <input |
| 149 | + value={values.firstName} |
| 150 | + name="firstName" |
| 151 | + onChange={handleChange} |
| 152 | + /> |
| 153 | + </label> |
| 154 | + |
| 155 | + <label> |
| 156 | + Last Name |
| 157 | + <input |
| 158 | + value={values.lastName} |
| 159 | + name="lastName" |
| 160 | + onChange={handleChange} |
| 161 | + /> |
| 162 | + </label> |
| 163 | + </form> |
| 164 | + </div> |
| 165 | + ) |
| 166 | +} |
| 167 | + |
| 168 | +export default Welcome |
| 169 | +``` |
| 170 | + |
| 171 | +### Test 1: Render the component, and check if the `h1` value is correct |
| 172 | + |
| 173 | +**Enzyme test** |
| 174 | + |
| 175 | +```jsx |
| 176 | +test('has correct welcome text', () => { |
| 177 | + const wrapper = shallow(<Welcome firstName="John" lastName="Doe" />) |
| 178 | + expect(wrapper.find('h1').text()).to.equal('Welcome, John Doe') |
| 179 | +}) |
| 180 | +``` |
| 181 | + |
| 182 | +**React Testing library** |
| 183 | + |
| 184 | +```jsx |
| 185 | +test('has correct welcome text', () => { |
| 186 | + render(<Welcome firstName="John" lastName="Doe" />) |
| 187 | + expect(screen.getByRole('heading')).toHaveTextContent('Welcome, John Doe') |
| 188 | +}) |
| 189 | +``` |
| 190 | + |
| 191 | +As you see, both are pretty similar, Enzyme's `shallow` wrapping doesn't descend |
| 192 | +down to sub-components, React Testing Library's `render` is more similar to |
| 193 | +`mount`. |
| 194 | + |
| 195 | +In React Testing Library, you don't need to assign the `render` result to a |
| 196 | +variable (wrapper, or etc), and you can simply access the rendered output by |
| 197 | +calling the `screen`. The other good thing to know is that React Testing Library |
| 198 | +automatically cleans up the output for each test, so you don't need to call |
| 199 | +`cleanup` on Jest's `afterEach` or `beforeEach` function. |
| 200 | + |
| 201 | +The other thing that you might notice is `getByRole` which has `heading` as its |
| 202 | +value. `heading` is the accessible role of the `h1` element. You can learn more |
| 203 | +about them on |
| 204 | +[queries](https://testing-library.com/docs/dom-testing-library/api-queries#byrole) |
| 205 | +documentation page. One of the things people quickly learn to love about Testing |
| 206 | +Library is how it encourages you to write more accessible applications (because |
| 207 | +if it's not accessible, then it's harder to test). |
| 208 | + |
| 209 | +### Test 2: Input texts must have correct value |
| 210 | + |
| 211 | +In the component above, the input text value will be initialized with the |
| 212 | +`props.firstName` and `props.lastName` values, and we need to check whether the |
| 213 | +value is correct or not |
| 214 | + |
| 215 | +**Enzyme** |
| 216 | + |
| 217 | +```jsx |
| 218 | +test('has correct input value', () => { |
| 219 | + const wrapper = shallow(<Welcome firstName="John" lastName="Doe" />) |
| 220 | + expect(wrapper.find('input[name="firstName"]').value).to.equal('John') |
| 221 | + expect(wrapper.find('input[name="lastName"]').value).to.equal('Doe') |
| 222 | +}) |
| 223 | +``` |
| 224 | + |
| 225 | +**React Testing Library** |
| 226 | + |
| 227 | +```jsx |
| 228 | +test('has correct input value', () => { |
| 229 | + render(<Welcome firstName="John" lastName="Doe" />) |
| 230 | + expect(screen.getByRole('form')).toHaveFormValues({ |
| 231 | + firstName: 'John', |
| 232 | + lastName: 'Doe', |
| 233 | + }) |
| 234 | +}) |
| 235 | +``` |
| 236 | + |
| 237 | +Cool! It's pretty simple and handy, and the tests are clear enough that we don't |
| 238 | +need to talk so much about them. But something that you might notice is that the |
| 239 | +`<form>` had a `role="form"` attribute, but what is it? |
| 240 | + |
| 241 | +`role` is one of the accessibility-related attributes that is recommended to use |
| 242 | +to improve your web application for people with disabilities. Some elements have |
| 243 | +default `role` values and you don't need to set one for them, but some others |
| 244 | +like `<div>` do not have one. You could use different approaches to access the |
| 245 | +`<div>` element, but we recommend trying to access elements by their implicit |
| 246 | +`role` to make sure your component is accessible by people with disabilities and |
| 247 | +those who are using screen readers. This |
| 248 | +[section](https://testing-library.com/docs/dom-testing-library/api-queries#byrole) |
| 249 | +of the query page might help you to understand better. |
| 250 | + |
| 251 | +> Keep in mind that a `<form>` must have a `name` attribute in order to have an |
| 252 | +> implicit `role` of `form` (as required by the specification). |
| 253 | +
|
| 254 | +React Testing Library aims to test the component, like how users would, users |
| 255 | +see the button, heading, form and other elements by their role, not by their |
| 256 | +`id` or `class`, or element tag name. When you use React Testing Library, you |
| 257 | +should not try to access the DOM like how you would do by |
| 258 | +`document.querySelector` API (which you can incidentally use in your tests, but |
| 259 | +it's not recommended for the reasons stated in this paragraph). |
| 260 | + |
| 261 | +We made some handy query APIs which help you to access the component elements |
| 262 | +very efficiently, and you can see the |
| 263 | +[list of available queries](https://testing-library.com/docs/dom-testing-library/api-queries) |
| 264 | +in detail. |
| 265 | + |
| 266 | +Something else that people have a problem with is that they're not sure which |
| 267 | +query should they use, luckily we have a great page which explains |
| 268 | +[which query to use](https://testing-library.com/docs/guide-which-query), so |
| 269 | +don't forget to check it out! |
| 270 | + |
| 271 | +If you still have a problem with the React Testing Library's queries, and you're |
| 272 | +not sure which query to use, then check out |
| 273 | +[testing-playground.com](https://testing-playground.com) and the accompanying |
| 274 | +Chrome extension |
| 275 | +**[Testing Playground](https://chrome.google.com/webstore/detail/testing-playground/hejbmebodbijjdhflfknehhcgaklhano/related)** |
| 276 | +that aims to enable developers to find a better query when writing tests, and it |
| 277 | +helps you find the best queries to select elements. It allows you to inspect the |
| 278 | +element hierarchies in the Chrome Developer Tools, and provides you with |
| 279 | +suggestions on how to select them, while encouraging good testing practices. |
| 280 | + |
| 281 | +## using act() and wrapper.update() |
| 282 | + |
| 283 | +In Enzyme, for some asynchronous purposes, you have to call `act()` to run your |
| 284 | +tests correctly, but in React Testing Library you don't need to use `act()` most |
| 285 | +of the time since it wraps APIs with `act()` so you don't need to manually wrap |
| 286 | +it. |
| 287 | + |
| 288 | +`update()` syncs the Enzyme component tree snapshot with the react component |
| 289 | +tree, often time you might see `wrapper.update()` in Enzyme tests, but React |
| 290 | +Testing Library does not need something like that, good for you since you need |
| 291 | +to handle fewer things! |
| 292 | + |
| 293 | +## Simulate user events |
| 294 | + |
| 295 | +There are two ways to simulate user events, one is a perfect library named |
| 296 | +[`user-event`](https://github.com/testing-library/user-event), and the other way |
| 297 | +is to use |
| 298 | +[`fireEvent`](https://testing-library.com/docs/dom-testing-library/api-events). |
| 299 | +`user-event` is actually built on top of `fireEvent` which simply calls |
| 300 | +`dispatchEvent` on the element given. However, `user-event` is generally |
| 301 | +recommended because it ensures that all the events are fired in the correct |
| 302 | +order for typical user interactions which helps to ensure your tests resemble |
| 303 | +the way your software is used. |
| 304 | + |
| 305 | +To use the `@testing-library/user-event` module, you first need to install it: |
| 306 | + |
| 307 | +``` |
| 308 | +npm install --save-dev @testing-library/user-event @testing-library/dom |
| 309 | +``` |
| 310 | + |
| 311 | +Now you can import it into your test: |
| 312 | + |
| 313 | +```jsx |
| 314 | +import userEvent from '@testing-library/user-event' |
| 315 | +``` |
| 316 | + |
| 317 | +To demonstrate how to use `user-event` library, imagine we have a component |
| 318 | +named Checkbox, and it only shows a checkbox, and we want to simulate the user |
| 319 | +event with this component, here is the component: |
| 320 | + |
| 321 | +```jsx |
| 322 | +// Checkbox.js |
| 323 | +import React from 'react' |
| 324 | +const Checkbox = () => { |
| 325 | + return ( |
| 326 | + <div> |
| 327 | + <label htmlFor="checkbox">Check</label> |
| 328 | + <input id="checkbox" type="checkbox" /> |
| 329 | + </div> |
| 330 | + ) |
| 331 | +} |
| 332 | + |
| 333 | +export default Checkbox |
| 334 | +``` |
| 335 | + |
| 336 | +And here we want to test when a user click on the checkbox, does the value |
| 337 | +change to “checked”? So, let's see how we write a test for that case: |
| 338 | + |
| 339 | +```jsx |
| 340 | +test('handles click correctly', () => { |
| 341 | + render(<Checkbox />) |
| 342 | + |
| 343 | + userEvent.click(screen.getByText('Check')) |
| 344 | + expect(screen.getByLabelText('Check')).toBeChecked() |
| 345 | +}) |
| 346 | +``` |
| 347 | + |
| 348 | +Nice! With the help of other modules provided by the Testing Library, we can |
| 349 | +test our components smoothly! To learn more about the `user-event` library, you |
| 350 | +can have a look at its |
| 351 | +[GitHub repository](https://github.com/testing-library/user-event#table-of-contents) |
| 352 | + |
| 353 | +### Triggering class methods in tests (`wrapper.instance()`) |
| 354 | + |
| 355 | +As we already discussed, we are against testing implementation details and |
| 356 | +things that uses are not aware of it, and we aim to test and interact with the |
| 357 | +component like how our users would. |
| 358 | + |
| 359 | +> if your test uses instance() or state(), know that you're testing things that |
| 360 | +> the user couldn't possibly know about or even care about, which will take your |
| 361 | +> tests further from giving you confidence that things will work when your user |
| 362 | +> uses them. |
| 363 | +> [Kent C. Dodds](https://kentcdodds.com/blog/why-i-never-use-shallow-rendering#calling-methods-in-react-components) |
| 364 | +
|
| 365 | +If you're unsure how to test something internal within your component, then take |
| 366 | +a step back and consider: "What does the user do to trigger this code to run." |
| 367 | +Then make your test do that. |
| 368 | + |
| 369 | +### How to `shallow` render a component? |
| 370 | + |
| 371 | +Generally, you should avoid mocking out components. However, if you need to, |
| 372 | +then it's pretty trivial using |
| 373 | +[Jest's mocking](https://jestjs.io/docs/en/manual-mocks.html) feature. (see our |
| 374 | +[FAQ](https://testing-library.com/docs/react-testing-library/faq)) |
0 commit comments