Skip to content

Commit

Permalink
Post goodbye enzyme (#15)
Browse files Browse the repository at this point in the history
* chore(deps): install twitter embed

* post(goodbye-enzyme): initial content

* post(goodbye-enzyme): add content

* post(goodbye-enzyme): add content + update banner

* post(goodbye-enzyme): update post date

* post(goodbye-enzyme): update post content
  • Loading branch information
vernak2539 committed Apr 19, 2024
1 parent b8c2e70 commit f9fca4d
Show file tree
Hide file tree
Showing 4 changed files with 713 additions and 307 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@algolia/autocomplete-js": "^1.17.0",
"@algolia/autocomplete-theme-classic": "^1.17.0",
"@algolia/client-search": "^4.23.3",
"@astro-community/astro-embed-twitter": "^0.5.4",
"@astrojs/mdx": "2.3.1",
"@astrojs/react": "3.3.0",
"@astrojs/rss": "4.0.5",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Banner/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const iconMap: IconMap = {
}

> p:first-child {
margin-top: 1px;
margin-top: 2px;
}

> p:not(:first-child) {
Expand Down
170 changes: 170 additions & 0 deletions src/content/blog/2024/04/good-by-enzyme.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
title: "Goodbye Enzyme"
description: "You won't be missed..."
pubDate: "2024-04-19"
tags:
[
"frontend",
"javascript",
"typescript",
"react",
"enzyme",
"react testing library",
"testing",
]
---

<style>
{`.twitter-tweet {
margin-left: auto;
margin-right: auto;
`}
</style>

import { Tweet } from "@astro-community/astro-embed-twitter";
import Banner from "../../../../components/Banner/index.astro";

I've showed my age in the [previous post](./snapshot-testing-sucks.md), so I won't do it again. But, for the
purposes of this post, I will say that I've been a professional in the industry prior to [Enzyme](https://enzymejs.github.io/enzyme/),
during its height, and after it (i.e. now).

This leads me to say, using my _expert_ opinion, "Goodbye Enzyme, and good riddance. You will not be missed."

That may seem harsh, but I stand behind it. I've recently removed Enzyme from multiple, highly used codebases at my work
and have see the horrors caused by Enzyme. I'll cover some of these in this article, comparing it to [React Testing Library][rtl].

## Enzyme? React Testing Library (RTL)? What are they?

They are both testing frameworks aiming to make it easier to test React components. They both have their own philosophies
and ways of doing things.

### Enzyme

> Enzyme is a JavaScript Testing utility for React that makes it easier to test your React Components' output.
> You can also manipulate, traverse, and in some ways simulate runtime given the output.
### RTL

> You want to write maintainable tests that give you high confidence that your components are working for your users.
> As a part of this goal, you want your tests to avoid including implementation details so refactors of your components
> (changes to implementation but not functionality) don't break your tests and slow you and your team down.
<Tweet id="https://twitter.com/kentcdodds/status/977018512689455106" />

These differing philosophies are the main reason why I prefer RTL over Enzyme. Enzyme is more focused on the "output" of
a component. Additionally, they talk about traversing, manipulating, and simulating the output, which does not align
with how actual users would be interacting with it.

On the other hand, RTL is more focused on the "functionality" of a component. It is more concerned with how a user would
interact with the component, and how the component would respond to that interaction. ❤️

<Banner type="info">
I'm going to use personal anecdote. These may not match your experience.
</Banner>

## Problems with Enzyme

### Testing async scenarios is difficult

One of the biggest issues I've had with Enzyme is testing async scenarios. Unless you're purely working on a presentational component, most components will have some form of async natur. Something like data loading, form input and validation, etc.

You'll need to perform an action, wait for the async action to complete, then move on with your test.

This is a nightmare to do in Enzyme. Code using `setTimeout`, `setInterval`, `setImmediate`, `await Promise.resolve()` or similar is likely trying to account for these async actions. It's HACKY... and I never liked doing it, even though there was no other way.

RTL handles this much better in two ways, maybe more.

First, RTL provides a `waitFor` utility. It allows you to wait for a condition to be true before moving on with the test. This can be used in a sync or async manner. Using it in an async manner will ensure that the async action has completed before moving on.

Second, the [`@testing-library/user-event` package](https://testing-library.com/docs/user-event/intro/) provides utility methods to help deal with user interactions. When a user interaction triggers an async action via a command like `click`, `type`, `clear`, etc., the command can be awaited to ensure the async action has completed.

This functionality is only in the newer versions of `user-event`, but it's already proved amazingly helpful.

### Simulating interactions

When testing user interactions, Enzyme simulates events in a programmatic way by dispatching DOM events.

IMO, this doesn't mimic real user interaction as there may be multiple events that can fire in a single interaction.

For example, how many events fire when clicking a button? Well, there's quite a few:

1. `mouseover`
2. `mousedown`
3. `mouseup`
4. `click` (interesting that this is after `mouseup`... hmmm haha)
5. `mouseout`

If you're only simulating a "click" you're missing out on a lot of the events that would happen during an interaction. This leads
to less confidence in your tests (IMO).

<Banner type="info">
Checkout my in-depth post about [Simulating
Events](../../2023/04/simulating-js-events.mdx) for more context!
</Banner>

Or, if we take an example directly from their [website](https://testing-library.com/docs/user-event/intro/):

> For example, when a user types into a text box, the element has to be focused, and then keyboard and input events are fired and the selection and value on the element are manipulated as they type.
RTL, on the other hand, uses the `@testing-library/user-event` package to simulate user interactions (you can still use the
`fireEvent` method though if you really want to). This takes into account the multitude events that could happen when a user
interacts with a component.

### Having access to props

This is a killer... and I hate it. I've seen it abused so many times.

You can see a basic form below.

```js
const Component = ({ onSubmit }) => {
return (
<form onSubmit={onSubmit}>
<input type="text" name="input1" />
<input type="text" name="input2" />
<button type="submit" onSubmit={onSubmit}>
Submit
</button>
</form>
);
};
```

It's likely there's some form of validation on the inputs and once the form is submitted some action has to take place.

In Enzyme, you can access the props of a component and call the `onSubmit` function directly, allowing something like:

```js
it("should submit form", () => {
const someAsyncAction = jest.fn();
const onSubmit = () => {
someAsyncAction();
};
const wrapper = shallow(<Component onSubmit={onSubmit} />);

wrapper.find("form").props("onSubmit")();

expect(someAsyncAction).toHaveBeenCalled();
});
```

This is hard to look at. There's no user interaction and no validity checks. We're just calling a function directly (yes,
I have seen this in real life).

While this is a simple example, you can see how it would open the door for lazy testing (and it does).

## /endrant

While Enzyme was an absolute _killer_ when it first came out, enabling the testing of UI components like we'd not seen before, it's
not fit for purpose anymore.

It actively encourages bad practices, can't be used past v16 of React (with an official adapter),
isn't maintained, and has a philosophy that doesn't align with how users interact with components.

Please, please consider using [RTL][rtl] for your testing needs. It's a much better framework that encourages better testing.

Goodbye Enzyme, and good riddance. You will not be missed.

<script async src="https://platform.twitter.com/widgets.js"></script>

[rtl]: (https://testing-library.com/docs/react-testing-library/intro/).

0 comments on commit f9fca4d

Please sign in to comment.