James Calmus
24 Jun 2021
β’
5 min read
In my last article, I wrote about setting up a simple Next.js project with Jest, a great JavaScript testing library that is perfect for beginners who have never tested their code before.
Jest itself can be great for testing a wide range of JavaScript files, but when it comes to React β the framework Next.js sits upon β we can use tools that build on the test runner's functionality to target our components by rendering them, something Jest doesn't do out of the box.
Luckily, Jest has an active and passionate community, which means there are a number of incredibly useful tools that can extend Jest's API to allow the test runner to be used for just this purpose.
One such tool is React Testing Library, an open-source tool that I'm going to walk you through setting up and using with your Next.js project.
Let's start where we left off, with the project we created in the last article. Alternatively, you can grab the code from here.
First we're going to install the packages we need to start testing our components and pages.
Open your terminal, navigate to the project folder and type the following command:
$ yarn add -D @testing-library/jest-dom @testing-library/react
This will install the two Testing Library packages as developer dependencies, i.e. packages that aren't needed for your production site to work.
In this project, we have a single page, index.js
, which you can see by running yarn dev
in the terminal.
You can visit localhost:3000
in your browser when your terminal shows the following message:
event - compiled successfully
You should see a page appear with the title "Welcome to Next.js!" and four large links.
This is the page we're going to test with our newly-installed package, React Testing Library.
You can close your browser window now and type Ctrl+C
into your terminal. This should kill the yarn dev
instance. We don't need it to run our tests.
If you've used the template repository I linked to above, you should have a tests
folder with a single file in it, my-first-test.test.js
. If not, create the folder in your root directory. Inside, we're going to make a new file. Call it my-first-react-test.test.js
.
Jest, our test runner, has a very specific vocabulary when it comes to writing tests.
Tests can be split up into describe
blocks, a defined scope that helps us to identify the feature or component we're testing, and it
blocks, a function that encloses a specific test for that feature or component. If that doesn't make a lot of sense to you right now, it will shortly.
Let's write our first describe
block.
Type the following into my-first-react-test.test.js
:
// my-first-react-test.test.js
describe("Index page", () => {
});
As you can see, the describe block takes two arguments: the first is a string
that represents the feature or component we're testing. In this case, that's the index
page.
The second argument is a callback function, which encloses the series of tests we're going to run. Let's add one.
Add an it
block inside the describe
block that looks like the below:
// my-first-react-test.test.js
describe("Index page", () => {
it("should render", () => {
});
});
Again, the it
block takes two arguments, a description and a callback which will contain the assertions that will determine whether the test will succeed or fail.
You can start to see from simply reading the test above that there is a logic to Jest's vocabulary. We are describing a feature or component and asserting that it should do something.
In this case, we're going to be checking that the index page renders, which is a good initial test for us to write. If that test fails alongside more complex tests when you eventually get to writing them, you know something fundamental has gone wrong with your component, your test runner or your set-up.
Let's add some detail to our test.
Installing React Testing Library gives us access to a render
function we can call to, well, render the component we'd like to test. In this case, that's the index
page.
First, import a few essentials at the top of the test file:
// my-first-react-test.test.js
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
...
We need the first import, jest-dom
, to add some extra functionality to Jest. In particular, we're going to be making use of a function called toBeInTheDocument
that will help us to assert our component is rendering correctly. This is called a custom matcher.
We're also importing the render
function from @testing-library/react
which allows us to take control of the testing environment and specify exactly what should be exposed to Jest, and the screen
object, which makes finding our rendered component and its children incredibly easy.
Next, let's import the index
page so we can test it:
// my-first-react-test.test.js
...
import Index from '../pages/index';
...
Inside the it
block, call the render function with the Index page as an argument, just as you would write it to render it in a React component:
// my-first-react-test.test.js
...
describe("Index page", () => {
it("should render", () => {
render(<Index />);
});
});
...
Let's add an expectation.
If you look in the index.js
file, you'll see we have a component that looks something like this:
// pages/index.js
export default function Home() {
return (
<div className={styles.container}>
<Head>
...
</Head>
<main className={styles.main}>
...
</main>
</div>
)
}
Our index
page is split into two sections, Head
and main
. The former is a Next.js component that allows you to define the structure of your page's metadata directly in your jsx, and the latter is an HTML tag that defines the main content of your HTML page. Because our test is about the page rendering correctly, we're going to search for the main
tag in our test.
Head back to my-first-react-test.test.js
and, underneath your render
function, create a variable called main
that is the result of searching for the role "main" on the screen:
// my-first-react-test.test.js
...
it("should render", () => {
render(<Index />);
const main = screen.getByRole("main");
});
...
Now, we want to expect that element to have appeared in our render:
// my-first-react-test.test.js
...
it("should render", () => {
render(<Index />);
const main = screen.getByRole("main");
expect(main).toBeInTheDocument();
});
...
Now, let's test this is all working. In your terminal, type the following command:
$ yarn jest my-first-react-test
You should see a green badge with the word PASS
alongside the name of our test. This shows that our page is rendering correctly and our expectation has been met.
If you'd like to prove this is the case, change the expectation to the opposite assertion, that the main
element is not in the document, like so:
// my-first-react-test.test.js
...
it("should render", () => {
render(<Index /);
const main = screen.getByRole("main");
expect(main).not.toBeInTheDocument();
});
...
Run the test again, and this time you'll see the word FAIL
, in your terminal along with a detailed description of what went wrong. In this case, our output shows that main
was in the document, wrapping the body of our page.
Remove the not
to enable your test to pass again, and you're now ready to go on and write more complex tests with Jest and Next.js.
The test you've written above is incredibly simple, but it shows us that you can render a React component in a test and expect something of it.
For more detail on the expectations you can make with our setup, I recommend reading the jest-dom docs which has a full list of all of the custom matchers included in it.
I'd also recommend reading the React Testing Library docs which give you more information on how to use the library, and how to use tools like data-testid
to expose your components to your tests.
If you'd like the code we produced in this guide, you can find a repository here.
James Calmus
See other articles by James
Ground Floor, Verse Building, 18 Brunswick Place, London, N1 6DZ
108 E 16th Street, New York, NY 10003
Join over 111,000 others and get access to exclusive content, job opportunities and more!