Categories
React

React Testing — Getting Started

Spread the love

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.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *