Migration guide
Enzyme → React Testing Library, step by step.
Enzyme doesn't work on React 18 and hasn't been maintained in years. React Testing Library is where the ecosystem went. This isn't a search-and-replace: RTL tests what a user sees, not your component internals, so most tests get rewritten rather than renamed.
Refactyl doesn't migrate Enzyme to React Testing Library yet. This guide covers the manual path; the waitlist notifies you when the verified engine ships.
Why this migration is hard to get right
shallow() has no RTL equivalent
Enzyme's shallow() renders a component one level deep, isolating it from children. RTL always full-renders with render(). Tests that relied on shallow to avoid child component side effects (network calls, context requirements, complex renders) must be refactored with mocked children or proper test context providers.
wrapper.find('.my-class') tests implementation details
Enzyme's selector API encourages finding elements by CSS class, component name, or prop value, all internal details a user never sees. RTL's query API (getByRole, getByText, getByLabelText) finds elements by what a user perceives. Every Enzyme query needs rewriting to a semantics-first query, which often means updating the component's markup to have proper ARIA roles or accessible labels.
.simulate() doesn't fire real events
Enzyme's .simulate('click') calls the onClick prop directly. It doesn't fire a real DOM event. RTL's fireEvent.click() and userEvent.click() fire real bubbling DOM events. Tests that relied on Enzyme's shortcut simulation may produce different results when real event propagation is involved.
How to migrate manually
Install RTL, remove Enzyme
Install @testing-library/react, @testing-library/jest-dom, @testing-library/user-event. Remove enzyme, enzyme-adapter-react-16/17/18, @types/enzyme. Remove enzyme setup from jest.config.js / setupFiles.
Add jest-dom matchers
Add `import '@testing-library/jest-dom'` to your test setup file. This gives you .toBeInTheDocument(), .toHaveValue(), .toBeVisible(), the idiomatic RTL assertion set.
Replace shallow() and mount() with render()
The RTL equivalent of both is render(). For tests that used shallow to isolate from children, add jest.mock() calls for the child components you want to stub. For tests that used mount().instance() to call methods directly, rewrite to trigger the behavior through user interaction.
Rewrite Enzyme queries to RTL queries
Replace wrapper.find('button').first() → screen.getByRole('button'). Replace wrapper.find('[data-testid="x"]') → screen.getByTestId('x'). Replace wrapper.find(MyComponent) → consider what the user sees; if nothing, the test is testing implementation. Priority: getByRole > getByLabelText > getByText > getByTestId.
Replace simulate() with userEvent or fireEvent
Replace wrapper.simulate('click') → await userEvent.click(element). Replace wrapper.simulate('change', { target: { value: 'x' } }) → await userEvent.type(element, 'x'). Use userEvent (async) for all user interactions; use fireEvent only when you need exact synthetic event control.
What Refactyl's verified version will do
Roadmap
Refactyl doesn't run this migration yet. When it ships, the bar will be the same as every other Refactyl path: every file gated by a real jest / vitest (RTL is a testing helper, not a runner) run, repaired until clean, or clearly flagged with the reason. No unverified files ship.
The public benchmark methodology is at refactyl.com/benchmark . The same dual-metric approach will apply here.
Manual / DIY vs Refactyl (roadmap)
This table describes what Refactyl's Enzyme→React Testing Library engine will do. It isn't live yet.
| Concern | Manual / DIY | Refactyl (roadmap) |
|---|---|---|
| React 18+ compatibility | Enzyme: no official React 18 adapter | RTL supports React 18 natively. Migration is required, not optional |
| Query rewrite scope | Every wrapper.find() needs manual rethinking, not just renaming | Pattern detection will identify automatable vs judgment-call rewrites, flagging the latter |
| Event simulation | Manual .simulate() → userEvent audit across the test suite | Will convert each event type to the async userEvent pattern |
| Test setup files | Remove enzyme-adapter config, add jest-dom setup manually | Setup migration will be handled as part of the batch |
FAQ
Enzyme → React Testing Library migration: common questions
Real questions engineers ask before migrating Enzyme to React Testing Library, answered honestly.
On the roadmap
Want Refactyl to do this for you?
Join the waitlist and we'll notify you when the Enzyme→React Testing Library engine ships. The bar is the same as every other Refactyl path: every file gated by a real jest / vitest (RTL is a testing helper, not a runner) run before handoff.
Refactyl doesn't migrate Enzyme to React Testing Library yet. No misleading trial, no fake demo.
Other migrations