Time to say goodbye - Enzyme.js
Piotr Staniów • Posted on 03 Jul 2021 • 10 mins
This article is a part of Thoughts series.
Thanks to the kindness of InfoQ there's also a Chinese translation available.
Let me start with a bold statement: It's time to deprecate Enzyme.js.
While of course this is an opinion rather than a fact, I think the overall React ecosystem and community would benefit from that move.
In 2019, when I was working in AWS CloudWatch UI team, I was the person behind introduction of React Testing Library, and I was frequently and passionately advocating for replacing Enzyme with it. It's certainly never easy to get people excited to learn (yet another!) new JavaScript library that does the same thing. Yet, when I was leaving Amazon, I had impression this was a successful move, and RTL was steadily gaining traction in my team's projects.
There are however many more companies in the world using React, and they would vastly benefit from ditching Enzyme too! Let's have an honest discussion about how the React world has changed in past 5 years, and why Enzyme should be deprecated.
What deprecation means in software development?
In the programming context, deprecation simply means communicating to other developers that a piece of software is no longer a preferred solution, and that it has been superseded by newer code.
It doesn't mean that you should immediately stop working on anything else and rewrite code to use newer software, and it also doesn't always mean the deprecated software will be unmaintained.
However, deprecation expresses the intent that we want to gradually and organically transition to using newer solution, as we see it more beneficial, either to users or to developers experience.
Today, there are two competing libraries for testing React components: Enzyme and React Testing Library, and I would like to convince you that Enzyme shouldn't be used in your new code.
A bit of React's history
Let's quickly recap what was happening in React world in the last 5 years.
In 2016 AirBnB has first released Enzyme – their own React testing library. At that time, AirBnB was one of the leaders in the React ecosystem, heavily contributing to numerous open source projects. For many their tools became a de facto standard in the industry, and without any doubt they empowered countless developers. They have also redefined what it means to write modern JavaScript code, by influencing developers code style through popular ESLint AirBnB preset.
It'd be hard for me to imagine what would it be like back then to write tests if it wasn't for Enzyme. Even with Enzyme, it was noticeably harder than it is today, and I still vividly remember installing and configuring in my project Mocha, Chai, Sinon, and JSDOM, to get pretty much the same tooling that today a single library – Jest – provides out of the box, while adding numerous features on top of that.
The world has already moved on. In April 2018 (wow, that's already 3 years?!), Kent C. Dodds has announced on his blog the release of React Testing Library, which drastically changes the approach to writing unit tests in React.
In February 2019, React team has released the new, long anticipated version of React (16.8) that has transformed the API significantly and introduced React Hooks. In reality, this has completely changed how modern React code is written, and countless libraries in the ecosystem were rewritten or amended to support hooks.
We all have changed our thinking about function-based components from being stateless and presentational-only, to being a fully-fledged alternative to class-based components. Many React tutorials became obsolete at that moment too.
In February 2020, AirBnB has announced they are moving the ownership over Enzyme to an external GitHub organization. While they promise to also keep supporting Enzyme, they also mention that React Testing Library is getting traction amongst their projects:
While other options like React Testing Library have gained traction within Airbnb, we will still be using and contributing to Enzyme for the foreseeable future.
Fast forward to 2021, and there's currently exactly one developer maintaining Enzyme:
i am the only enzyme developer, to be clear.
There's also something more going in the background – React team has committed to completely rewrite React docs, giving it a long-deserved refresh. It will also represent the change in how modern React code looks like today, and this is using functional components with hooks.
Class components are going to be around for years to come—for example, there are tens of thousands in production at Facebook already. However, we do recommend that new apps be built with function components and Hooks, which is why we want those docs front and center. The class component docs will remain available for folks working with those components, and class components themselves might one day be spun out into their own package—but if that did happen, we're provide migration scripts to automate that transition :)
Why Enzyme should be deprecated?
There are a couple of reasons that can be summarized in a few bullets:
- It has a long record of falling behind changes in React, preventing people from transitioning to newer React versions
- It relies on React internal implementation, and React team discourages using it
- It is currently maintained by single person – and it's risky for so many companies to rely on a single person to maintain one of their crucial pieces of software
- It facilitates some bad testing practices, and tests in Enzyme don't represent customer experience
- There's a far better solution in the market, and the industry has already moved on
New React features aren't supported
In my experience, in the past 3 years it was nearly always the case that Enzyme wasn't supporting the latest bits of React, and this wasn't changing for months.
If you're writing new code, I'd argue it's a good practice to use possibly newer versions of libraries, as they receive generally better support and go in par with the industry standards.
When you have a look at Enzyme's issue tracker it still doesn't fully support React 16, which was released about 3 years ago.
The support for React 17 is also an open issue for nearly a year now, and you can only choose amongst 6 unofficial adapters that try to provide support for React 17 to Enzyme, each solving and having their own subset of problems.
Just as a reminder, React 18 Alpha was recently released – and while everything may change, it's not unrealistic to think it might be shipped in a few months from now.
What's a weaker point of Enzyme is there are actually pieces of API within Enzyme that are only working with class-based components, and they have no equivalents for function-based ones.
The reality is that if you're writing new code today, you probably don't want to use Enzyme for testing it. Chances are, that if you're writing modern React code – you will sooner or later encounter issues (like this one or that one). It usually ends up in either writing some hacks to work around the problem, or changing test scenarios to match library's capabilities, or leaving a code piece of untested.
It is discouraged by React core team
While this argument may or may not resonate with you, it's important for me that the experts behind React to us are discouraging the use of Enzyme, and recommend switching to React Testing Library.
After all, we all trust them in what they plan for the React's implementation to become and how they shape their APIs. So when they recommend using React Testing Library, they probably know what they are talking about.
There's a number of cues that the industry has moved on already. It seems that Facebook – the very company in which React was born – has frozen their Enzyme tests and banned using it in any new tests:
While tangential to the issue, I think it’s worth noting that if you can use a project like React Testing Library that doesn’t depend on React internals, it’s generally a good idea. At FB we’ve frozen Enzyme tests to stay on an old version of React that won’t be upgraded for this reason, and we banned using it in any new tests. I don’t want to hijack this issue with an unrelated discussion, but for people who feel blocked by this, I want to provide some encouragement that it’s a worthwhile investment to use a different approach.
If you wonder what is in the official React documentation today, the recommendation is to actually use React Testing Library. Certainly, there are circumstances in which you won't be able to use RTL, I get it. There's surely added cost to training your developers on new technology, there will be some friction around that. You probably won't also devote hundreds of developer-hours to rewrite your tried-and-tested in battle unit tests, potentially introducing bugs to them.
And that is fine! There's no rush for a change needed, this is not a security issue.
I read this as a significant message though – experts and community behind React have assessed options and made their choice. If you don't want to make a profound research on why RTL is better than Enzyme, the official docs is your first resource to go to for knowledge.
It is maintained by a single person
As of today, Enzyme is maintained by a single person – Jordan Harband. He's a prolific open source contributor, member of TC39 committee (they specify JavaScript) and really a hero, who is single-handedly maintaining what empowers millions of tests suites around the world.
The reality though is, he's presumably splitting his time between many projects, and he has a personal life like all of us do. It is unreasonable to expect that a single person can maintain something so core to developer's work as Enzyme is, with all the nuances and edge-cases we encounter.
In fact, it is dangerous for any company to put so much responsibility on a single person. Even today, we see side effects of that situation: there are tests which weren't written, cases we spent hours debugging, or features we didn't use to simplify our codebase, because Enzyme isn't up-to-date. The other side of the coin is, we probably shouldn't put so much responsibility and pressure on anyone.
The problem is more complicated of course, and we should ask why out of thousands companies using Enzyme there's so few that are actually contributing back to the open source code. The same code that generates revenue for them. I think though the reality is that this won't change swiftly, and we have to deal with the present, far from ideal situation. Today, Enzyme has a single developer, and even if there were more – it would take many months to resolve all of Enzyme's problems.
It is somewhat easier to misuse
Amongst all unit tests I've seen written in Enzyme and React Testing Library, I would argue it is easier to misuse Enzyme than RTL.
They represent two different approaches, where Enzyme provides wrappers around components, and RTL is focused on rendering components "the way customers see them" (DOM representation).
Maybe it's because I've seen more tests in Enzyme throughout these years, but
I've seen too many examples of meaningless tests. Tests which were shallowly
rendering a component with a spy function as prop, which was next extracted
using Enzyme's .props()
method, called directly and asserted to be called.
Doesn't make sense? To me neither but things like that happen, and I think Enzyme makes it too easy to create empty lines coverage with such tests.
Even more frequently, I saw a snapshot of component's props, which tells absolutely nothing about the test case intent, and is tightly coupling tests with internal implementation details.
While certainly there are many ways to go sideways with React Testing Library, in my experience RTL is just a brilliant piece of software that makes people write more meaningful tests.
The industry has already moved on
React Testing Library is extremely well-thought in the aspect of "how others will use this library?" and "what practices do I encourage?". I prefer this approach over Enzyme – which is quite powerful but at the same time leaves too many ways of achieving the same goal.
One example is RTL's focus on testing the customers experience – after all, it is what really matters. Our customers don't see what props our components have, or if they're an array or an object, as long as it works – and will help you deliver value in the future.
This is also true about React Testing Library API which provides querying functions which let you grab DOM elements using accessibility features. Which is a really nice nudge to actually introduce them, and support an estimated 10% of users who may need these features to be on your website.
Writing such tests in RTL is also cheaper than any browser-based tests, as they
are almost never flaky, and are far easier to maintain. At the same time,
you're operating on native DOM elements (well, jsdom
implementation of those)
which is extremely powerful. It doesn't require you to learn any extra APIs,
there's just native browser's API we all know.
If you're considering how your tests should be split today, I'd encourage you to write as many tests in React Testing Library as you can. If something can't be tested in RTL (I promise drawing a rectangle in SVG with drag and drop can be!), then fall back to Puppeteer or Cypress – That would be for instance when you need actual styles to be computed, or make some end to end testing.
If RTL doesn't support something, you have entire DOM available for you. Unfortunately, Enzyme doesn't let you get underlying elements if they come from functional components but will throw an error instead.
Ultimately, the industry has already moved on. Enzyme in the past year had a steady number of weekly downloads at around 2.1-2.5M. At the same time, React Testing Library has gone from 1.8M to over 4M weekly downloads.
In the 2020 State of JS survey developers have delivered their opinion on React Testing Library – of those who already have heard of it:
- 58% of developers have used it and would use it,
- further 30% have heard of it and would like to use it.
Finally, GitHub Insights tool provides an interesting statistics on the usage of both. Amongst open source repositories hosted at GitHub, at the time of writing:
- 354,059 repositories are dependents of Enzyme
- 2,440,909 repositories are dependents of React Testing Library
What next?
It seems that the reality is that the industry has already made the shift towards React Testing Library, and this is for many and good reasons.
It's hard to tell how realistic it is to expect that Enzyme will be deprecated anytime in the near future, but it seems an inevitable move that probably is already visible in the horizon. While Enzyme is still used by many today, I would expect the numbers to decrease sharply pretty soon.
Deprecating the library today would help many companies avoid introducing unnecessary technical debt and would send a clear message to the entire community that the future is in React Testing Library.
It is not a message to say "immediately cease using it", and it is not to say "rewrite immediately all your tests". However, you probably shouldn't be covering newly written code with tests using Enzyme. Both libraries can safely co-exist, and you may simply ban new code from using Enzyme, letting developers organically and gradually transition when it's convenient.
The React's future is in function-based components, React Hooks, asynchronous rendering – and these features are best served today with React Testing Library. Looking at the past three years of Enzyme's path, it seems unlikely to catch up with all of these features, while also resolving its other problems.
It is time to deprecate Enzyme.