Professional Documents
Culture Documents
TBGRT Dark
TBGRT Dark
TBGRT Dark
Table of Contents
Changelog
v1.0.0 - 2021/02/18
Introduction
Setup
Steps
What are all the React testing tools and how do I use them?
Jest
Setting Jest up
Enzyme
Cypress
Page 2 of 25
Convincing your team to migrate
Unit Testing
Integration Testing
Solutions
Snapshot Testing
Solutions
Final Thoughts
Page 3 of 25
Changelog
v1.0.0 - 2021/02/18
Initial release
Page 4 of 25
Introduction
Welcome to The Beginner's Guide to React Testing.
The React ecosystem changes quite a bit, and if you're not actively on Twitter,
reddit, or following several newsletters, it can be pretty di cult to keep track of
what the latest best practice is.
If you think your friends or colleagues would bene t from this book, absolutely
feel free to share it with them.
If you still have questions after you read this guide, feel free to email me at
max@maxrozen.com and Tweet at me @rozenmd.
If you like what you read, you can nd more of my writing at MaxRozen.com.
Page 5 of 25
How do I get started
testing?
It can be pretty di cult to work out what exactly you should be testing when it
comes to writing tests in React. Particularly when half of the examples in
articles online seem to be doing it "the old way", rather than testing
functionality over implementation details.
Typically though, when I start writing tests for a new project, I start testing
entire screens where possible (assuming I'm working with a well-tested
component library), meaning I start with integration tests rst.
Page 6 of 25
Our React login form for testing
Setup
STEPS
2. Run:
yarn
# then
yarn start
Page 7 of 25
You should see something like this:
3. At this point, if you ran yarn test , you would see the following:
PASS src/pages/Login.test.js
✓ integration test (177ms)
Page 8 of 25
import React from 'react';
// fireEvent lets us click on our `Button` component
import { render, fireEvent, screen } from '@testing-
library/react';
// user-event lets us type into our inputs
import user from '@testing-library/user-event';
import Login from './Login';
render(<Login />);
fireEvent.click(submitButton);
// We use `findByText()` after performing an action
// that may be asynchronous (like filling out a form).
//
// findByText returns a Promise, letting us await
// until it finds the text it's looking for before continuing
expect(await screen.findByText(/your
username/i)).toBeInTheDocument();
Page 9 of 25
expect(await screen.findByText(/your
password/i)).toBeInTheDocument();
});
The above example likely looks complicated if you're new to integration testing,
but we've essentially taken the steps a user takes when logging in, and turned it
into a test:
We've built a test that can type into our TextField components, click on our
Button component, and trigger the Form component's onSubmit function,
without ever referring to implementation details!
React Testing Library also has a cheatsheet with tips to help you check which
function to use.
Jest
Page 10 of 25
React Testing Library
This chapter covered the "how" of testing React, the following chapters explain
the "what" and the "why".
Page 11 of 25
What are all the React
testing tools and how do I
use them?
Jest
Jest is a test runner that runs your tests in your terminal, with a number of
useful options for how to run your tests.
Setting Jest up
Page 12 of 25
npm install --save-dev jest
# OR
yarn add --dev jest
{
"scripts": {
"test": "jest"
}
}
To get Jest to work with React, you'll want to also follow the steps here.
From here, you can run yarn test to have Jest run your tests once, or run
yarn test --watch to enable watch mode, which runs tests only for les that
you're actively editing. This gives you a nice feedback loop when you're writing
code, and trying to avoid break things.
Jest by itself can't actually load React components, which is why Enzyme and
React Testing Library exist.
Enzyme
For a very long time, Enzyme was the library React developers used to test React
components. It lets you mount your components, and manipulate them for your
tests.
As a result of being around for so long, most legacy React projects likely still use
Enzyme.
Page 13 of 25
The issue with Enzyme is that it provides several APIs that encourage testing of
implementation details (such as shallow() , find() , instance() and
state() ), rather than functionality (can the user click our UI, and does the UI
change as a result?).
Cypress
Cypress is an all-in-one end-to-end testing solution (we'll discuss what end-
to-end testing is in a later chapter) - meaning it's both a test runner (like Jest),
and a set of APIs for testing (like React Testing Library).
Cypress launches a web browser, and actually clicks through your app. It
requires quite a bit more set-up to work properly than other test tools (such as
special test users, test instances for your app), though it also gives you
con dence that your app actually works.
On top of testing your entire web app from your computer, Cypress also
supports testing from their cloud as part of your app's CI/CD.
Page 14 of 25
Which testing framework
should I use (Enzyme or
React Testing Library)?
React Testing Library for new stu
For new projects, I would recommend using React Testing Library (RTL). This is
made easier by create-react-app, which now bundles RTL, and requires no
setup to start testing.
One thing you might want to install if you're setting up React Testing Library is
React Hooks Testing Library. It's made by the same people as React Testing
Library, with the same concepts, and makes testing React Hooks signi cantly
less complicated than other solutions.
If you're stuck using Enzyme, the best you can do is avoid testing
implementation details when using it (which will be a challenge, if you're new
to testing).
Particularly:
Rather than checking props and state, try to think of what your user can see,
and check that
Page 15 of 25
Try to avoid blindly copying existing Enzyme tests when writing your own
tests. They'll often follow outdated practices such as testing
implementation details (we didn't know any better at the time, sorry), and
won't actually improve your con dence that the app does what you expect.
For all old code, we'll leave the old solution as-is, unless we need to update
existing functionality in the old code. In other words, if you're editing an
existing le, take the e ort to refactor it to the new way of doing things.
Page 16 of 25
What are the different types
of tests we use in React?
The world of software testing has a lot of di erent tests to choose from. Luckily
for us in React, we typically only use three: Unit testing, Integration testing,
and End-to-end (E2E) testing.
I'll also cover snapshot testing and visual regression testing, although I
consider them to be forms of unit testing.
Unit Testing
Unit testing involves testing a unit (or chunk of code) in isolation, to gure out
whether it does what we expect. In React, typically the "unit" refers to a utility
function, function component, or class component.
function convertScoreToColour(score) {
const BAD = '#FF0000';
const AVERAGE = '#FFFF00';
const GOOD = '#00FF00';
Page 17 of 25
describe('convertScoreToColour', () => {
test('Numbers < 50 return the BAD colour', () => {
let output = convertScoreToColour(50);
expect(output).toBe('#FF0000');
});
test('Numbers > 50 but < 90 return the AVERAGE colour', () =>
{
let output = convertScoreToColour(75);
expect(output).toBe('#FFFF00');
});
test('All other numbers return the GOOD colour', () => {
let output = convertScoreToColour(92);
expect(output).toBe('#00FF00');
});
});
Despite this being a contrived example (that could use some refactoring),
there's quite a bit of bene t you can already get from testing a function in this
way.
For example, if we expect our function to only handle numbers less than 100,
we could write a test to check what happens when the number is greater than
100:
This test example would fail (the code we wrote would return the good colour in
this case), so you'd have to now update the function to match the behaviour you
expect in your tests.
Page 18 of 25
(PS: This example, where we wrote our test for numbers greater than 100 before
writing code is a form of test-driven development, which we'll cover in a later
chapter.)
Integration Testing
It helps to think of integration testing like a bigger unit test, except the unit
you're testing is the combination of several smaller components.
See the rst chapter - How do I get started testing? for a practical example of
integration testing a form.
For example, a typical E2E test may involve logging into your app, clicking
through a few screens, creating a few records, viewing them, deleting them,
then logging out.
Page 19 of 25
Due to how long E2E tests take to run and develop, we tend to only write E2E
tests for what some developers call "happy paths". Happy paths are the
journeys your users take through the app when everything goes right - no
errors, no warning messages, just checking that performing the correct steps
results in the correct response from the system.
Solutions
There are a few solutions in the E2E space worth checking out, particularly
Cypress and TestCafé.
There are pros/cons of each tool, depending on what sort of React app you work
on. The best way to know which one is for you is to try both, and see if it meets
your use case.
Snapshot Testing
Snapshot testing typically means taking a React component, rendering it to
string/HTML (so that we have text to save), and saving it as a baseline "good"
version. The next time you run your tests, your test runner (typically Jest)
compares the baseline version against what the React component looks like
now. If the two versions are the same, the test passes, otherwise it fails, and the
snapshot test needs to either be updated, or you accidentally updated the React
component and you need to undo your change.
For a short period of time, the React community considered snapshot testing
React components as a "Good Idea". It wasn't until most of our colleagues
blindly updated snapshots every time they changed that we realised perhaps it
wasn't such a good idea after all.
Page 20 of 25
updated, people just stopped checking what the changes were.
Used sparingly (for example, only for your building block components or
component library), visual regression testing is a powerful tool to avoid
unintentional changes within your design system or component library.
Solutions
There are a few solutions in the Visual Regression testing space as well - I've
personally used Chromatic for a design system in the past, and it saved me
hours of manual checks each week.
As always, I recommend giving both solutions a trial, and seeing which one you
like best.
Page 21 of 25
How do I make testing a
habit?
Use feedback loops
My favourite part of testing in React is the feedback loop I get from running Jest
in watch mode. By passing Jest the --watch ag (so yarn test --watch ), I'm
able to have jest only run tests for les that I change, and in doing so, I know in
the moment when I've broken something.
You might be wondering how this helps build a testing habit - the trick is, you
won't be able to deliver code if your tests aren't passing, so it forces you to run
tests as you code.
Page 22 of 25
What's a good level of code
coverage?
100% code coverage isn't required
As a developer, you might see some libraries in the JavaScript ecosystem with
100% code coverage. You might then wonder, if libraries aim for 100% code
coverage, why don't I?
Let me start by saying: Don't let coverage stop you from writing tests. You
might be thinking "Oh, but I'll never reach 100% code coverage, what's the
point?", but realistically, any tests are better than no tests.
For the apps we build day to day, 100% code coverage is often unrealistic, and
su ers from diminishing returns (as in, you'll get less bene t per test written
once you pass a certain percentage of code covered). As well as this, it's possible
to reach 100% code coverage without actually testing e ectively (particularly
when using Enzyme and shallow() ).
Think about it this way, if you were a user for the web app you build, what
functionality do you use every day, and would get upset if it stopped working?
That's the functionality you need to have covered by tests.
If you then hit 100% code coverage as a result of testing all of your app's
functionality - great! But don't let the number be your focus.
Page 23 of 25
Should I be doing test-
driven development (TDD)?
What I mean by TDD
To me, TDD means writing tests which fail before you start writing any code.
Once the tests are written, you start writing the code that makes the tests pass.
All of the tools described in this book support both a TDD and a non-TDD
approach.
If it works for you, great! I'd highly recommend giving it a try, to see if you like
it - but don't worry if you feel that it's not for you.
Page 24 of 25
Final Thoughts
That was the last chapter in the guide. I hope The Beginner's Guide to React
Testing was useful for you to get started.
As this book itself is version controlled (as all of my writing is), I'm always keen
to hear readers' thoughts - whether it's on how it can be improved, or general
comments.
If you'd like to get in touch with me, email and twitter are your best bets.
At the risk of sounding like a YouTuber by asking you to like and subscribe, I
also invite you to visit my website, to read more about React (and potentially
more web development topics in the future). You can also subscribe to my
newsletter to receive an article every two weeks with a single tip or trick to help
you in your career as a developer that works with React.
Thank you very much for reading The Beginner's Guide to React Testing, feel
free to share it with your colleagues, and discuss its ideas.
Cheers, Max.
Page 25 of 25