Automated tests are important for most apps.
In this article, we’ll take a look at how to write tests for React components.
Testing Tools
Create React App has test files and scripts built into the project.
First Test
We can write our first test by adding test files in our Create React App project.
If we have App.js
:
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
In App.test.js
, we write:
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
to add the test.
The render
function renders the App
component that we imported.
Then we call getByText
with a regex to get the element we’re looking for.
Finally, we call toBeInTheDocument
to check linkElement
is there.
Data Fetching
We add setup and teardown code with React tests.
Also, we can mock any HTTP requests in our test code so that we can run our tests in isolation.
For example, if we have the following component:
Answer.js
import React, { useState, useEffect } from "react";
export default function Answer(props) {
const [data, setData] = useState({});
async function fetchData() {
const response = await fetch("https://yesno.wtf/api");
setData(await response.json());
}
useEffect(() => {
fetchData(props.id);
}, []);
return (
<div>
<p>{data.answer}</p>
</div>
);
}
Then we have to mock the Fetch API.
To do that, we can write:
Answer.test.js
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import Answer from "./Answer";
let container = null;
beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
unmountComponentAtNode(container);
container.remove();
container = null;
});
it("renders answer data", async () => {
const fakeData = {
answer: "yes",
forced: false,
image: "https://yesno.wtf/assets/yes/6-304e564038051dab8a5aa43156cdc20d.gif"
};
jest.spyOn(global, "fetch").mockImplementation(() =>
Promise.resolve({
json: () => Promise.resolve(fakeData)
})
);
await act(async () => {
render(<Answer />, container);
});
expect(container.querySelector("p").textContent)
.toBe(fakeData.answer);
global.fetch.mockRestore();
});
In the code above, we have the container
which we use to mount our component.
We have the beforeEach
hook to create the container element to mount our component in.
And we have the afterEach
hook to unmount our component and remove the container.
Then in our 'render answer data'
test, we create the fakeData
object with our mock response data.
Then we mock the fetch
function in Answer.js
by using the jest.spyOn
method.
global
is the global object, 'fetch'
is the fetch
function.
mockImplementation
lets us mock fetch
with an object with the json
method that’s set to a function that returns a promise that resolves to the fake data.
Then we mount our Answer
component with act
and await
it to let the promise resolve.
And then we check what’s in the container
‘s p
element to check if it has what we expect.
Then finally, we call global.fetch.mockRestore()
to clear the mocks.
Conclusion
We can add tests and mock any data fetching code with Jest and React’s test library.