I'm the maintainer of @wojtekmaj/enzyme-adapter-react-17
. I'm here to warn you. Enzyme, a popular utility to test React components, is dead. It's time to move on. Here's why I think so.
For a long time, Enzyme has been the 1st choice when it comes to testing React applications, despite of how bumpy the road to Enzyme React 16 support was. Even today, 1/3 of React apps are still being tested using Enzyme!
How @wojtekmaj/enzyme-adapter-react-17
came to life
Back in August 2020, React 17 Release Candidate came out. Shortly after, an issue has been raised in Enzyme repository to add support for React 17. Immediately after, @layershifter has opened a PR adding an official enzyme-adapter-react-17
.
Unfortunately, there were (and, spoiler alert, still are) some issues with testing that prevented this PR from being merged.
Now, here's the thing. I'm the maintainer of many popular React packages, React-PDF, React-Calendar, and React-Date-Picker just to name a few. Professionally, I maintain several large projects, which collectively have more than 30,000 Enzyme-based unit tests.
I was in no position to wait for Enzyme to add support for React 17 to support React 17 in my packages. Rewriting all unit tests also looked like no fun to me. So, I've decided to act and publish a temporary adapter based on Oleksandr's work.
It wasn't only the adapter though: certain changes were also necessary in Enzyme adapter utils, which all Enzyme adapters depend on, so a fork of them was also needed. A couple of tireless evenings later, @wojtekmaj/enzyme-adapter-react-17
was born.
Fast-forward one year
The issue for adding React 17 support remains open and has become nothing but a source of bitter comments mixed with unproductive "any updates?". Oleksandr's PR remains unmerged. enzyme-adapter-react-17
is nowhere in sight. The unofficial adapter I published with an intention of being just a stepping stone before everyone eventually migrates to enzyme-adapter-react-17
, has become de facto the default adapter for React 17, with 16 million downloads so far.
Retrospectively, I'm not sure if publishing the package was a good decision.
On one hand, this move has helped thousands of developers worldwide to upgrade to React 17, relatively hassle-free.
On the other, I helped prolonging Enzyme's inevitable death and thus gave many developers hope and a sense of safety they shouldn't get to feel. And I'm sorry for that.
React 18
Before I came to this conclusion though, React 18 beta was announced, and of course, I jumped to see if @wojtekmaj/enzyme-adapter-react-18
could be released.
The big thing in React 18 are concurrent features. To opt-in, after upgrading to React 18, you also need to switch to the new createRoot
API:
// before
const container = document.getElementById('root');
ReactDOM.render(<App />, container);
// after
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App/>);
Until you switch to the new API, your app will behave as if it's running React 17. So if you don't plan to do this, there's no point of upgrading!
I quickly realized that the API changes in React 18 meant that releasing a React 18 Enzyme adapter will not be possible without a huge rework of not only the adapter itself, but also Enzyme, enzyme-adapter-utils
and enzyme-adapter-react-helper
.
Trust me, it's not gonna happen. It's over. No more "stepping stones". And certainly no more official adapters. Whether or not you plan to upgrade to React 18 in the near future, you should consider looking for Enzyme alternative right now.
What should I do? 😱
The answer is, as always, it depends. You don't have to upgrade React, after all.
Here's what I would do:
- Start familiarizing yourself with React Testing Library, an officially recommended library for React components.
- Make a rule to write new tests using RTL only.
- Consider making a rule to rewrite tests to RTL whenever you need to touch them and/or the component they are testing.
- In your designated time for repaying technical debt (you have designated time for repaying technical debt, right? …right?), rewrite your remaining Enzyme-based tests to RTL.
- Clean up your repo from Enzyme-specific bits
- When you're ready, upgrade to React 18.
A bit of personal advice
While Migrate from Enzyme support article is available, I suggest you to just start fresh, forgetting that Enzyme has ever existed. RTL is by no means an Enzyme drop-in replacement, so having a completely fresh mindset will help you getting the most of it.
Top comments (22)
Call me old-fashioned by in 10+ years of development with Rails I never seen a breaking change like this and I never had to rewrite an entire test suite... That is why I always fear the adoption of new trends.
I think the issue was because Enzyme dependent on internals of React, while tools like RTL test them from the outside, in Rails you have something similar, the test frameworks like RSpec are not dependent on Rails internals to work.
Enzyme did a bad thing there, RTL got it right, so right that testing-library has now a generic testing-library-dom and wrappers for different frameworks, theoretically a test for React could work with only a few changes to test also a Vue component, after the test render searching dom nodes and triggering events is the same.
I don't think that's a good excuse. Laravel gives you testing tools that interact with the internals of Laravel, and there are hardly ever any breaking changes there.
True though that the Laravel testing tools were created by the creators of Laravel and is officially endorsed, whereas Enzyme is completely unofficial and 3rd party to React.
Agreed. That FB doesn't really offer a testing solution is the problem here: people set out to build their own test libraries while FB was free to break them.
You make a good point. RTL is more of an integration testing library where as Enzyme has the ability to unit test components. This move from unit to integration test focus is a pretty big change for some applications.
Even though I see how the thinking behind integration x unit test is, I would recommend to watch Mario's talk on fragile tests (blog.devgenius.io/tdd-conference-2...).
The mindset of unit x integration around RTL is something that comes up often, and, it's possible to test a single component with RTL as well. The point is, this is not the philosophy of the library (testing-library.com/docs/guiding-p...).
In this sense, what I understand is: unit does not mean 1-1 (one test one component) rather, one behavior that you want to test.
Thanks for the great work on the adapter! I think we've all seen it coming for last couple of years now, given how long it took to get support for React 16.x. In fact, it still doesn't work in various scenarios, nonetheless it's a painful process to move away from it so we all wanted to avoid it.
I like how Facebook tackles it, keeping the old Enzyme tests alive but writing the new ones only in React Testing Library, and gradually upgrading the codebase when touched.
I wrote an article a few months ago (Time to say goodbye - Enzyme.js) to suggest that it's time to call the library deprecated, as it would help to convey the point about making such migration across companies. Alas, the idea didn't resonate with the last maintainer of Enzyme.
Maybe eventually the React core team will make that call on their docs page, that they discourage using it, who knows...
If someone still needs a real life project to estimate how big to change from Enzyme to RTL, you could check the following pr. It's a React 16 project upgrade to React 18.
github.com/react-atomic/reshow/pul...
The biggest challenge for me is the act function, I write some utils to handle it.
I also have a custom render function that makes it more like Enzyme.
Such as render().html() or render().instance()
util link:
github.com/react-atomic/reshow/blo...
FYI, publishing a unofficial package was a savior for me. I had to upgrade NextJS in a huge repo for vulnerability issues, which also upgraded React to v17
All tests for the last 3 years were done using rtl. But there is a considerable part of old tests that still are written using Enzyme. If it weren't for the adapter, the vulnerability issue would become a huge test-rewriting task.
Also, thanks for the warning. Will add the migration to rtl to our roadmap
A bit of shameless self-promotion, but also hoping to help some folks running into the challenge of migrating large repos away from enzyme.
If you are using
jest
andbabel
, you can consider using this babel plugin that allows you to incrementally adopt react 18 (without breaking existing tests that are using react 16 and enzyme):npmjs.com/package/babel-plugin-jes...
Thank you for this article! Doing migration from Enzyme in my project, I figured out there is a way to make this process more manageable and automated: thesametech.com/migrate-away-from-...
Please check out my article, and let me know what you think! I plan to work on my plugin and improve it further, for now, it's a beta version.
I came here expecting a rant and was surprised by genuinely interesting content. I had already moved on to RTL in the meantime, but still wondered what had happened. Thanks for satiating my curiosity.
I currently working on a project which uses RTL, coming from another company working with enzyme (and Preact). This project has like no view unit tests. At my old company I tested most of the view and most of the tests are done with shallow() for the main reason that we wanted to only test one and no other component. Now just realise that RTL has no shallow() and therefor does not even provide unit testing and instead only integration testing. That's kind of sad.
But it also shows IMO that the current direction of development of React, and sadly Preact too in some cases, went into a bad direction. As a maintainer for long term projects it is anoying that with every release they just change something because they can. Nowadays all that hooks are a good example how FB pushes new webdevs into a direction which causes a lot of blown up render functions which are not even not tested often, but also like I learned now, definitivly not unit tested.
I think I will put some effort in research for a new Framework. The only thing I need from React/Preact is the diff/renderer. Everything else is not used because it does not provide good coding pattern. For React it is even anoying that it does not support native events and everything is touched by React before you get it in your "hands". It is like a new jQuery.
Sorry, just frustrated.
It's really unfortunate that Enzyme is dead, although I can see why things came to this. Depending on the React internals has always been a minefield, and I suppose we finally stepped on a big one.
To date, RTL continues to frustrate me and comes up short in the ways that are the most valuable to me as a developer. It wouldn't be so bad if there was still a decent way to unit test components, but it seems that the React community at large has eschewed clean, self-contained unit tests in favor of these sweeping integration tests, which cover more ground, but with less depth and more blind spots. I would prefer to have a combination of both approaches, but alas...
I don't think I could disagree with that Kent C. Dodds opinion piece on testing implementation details any more than I already do. I wish that the industry at large had taken it with a larger grain of salt - as one valid approach instead of the only correct approach.
I guess I'll be dragged kicking and screaming into the future :')
RTL is simply worse than Enzyme.
I'll preface my next comment by saying I don't blame the Enzyme team at all, FOSS development is completely voluntary.
But it really feels like the only reason Enzyme is dying is because nobody wants to work on it, not because RTL is better. It just happens that Meta pays people to maintain RTL.