Testing React Context - Ultimate Guide

Bernard Bado
January 03, 2022
Testing React Context - Ultimate Guide
When you use affiliate links in this article, We earn a small comission. Thank you!

I often find myself staring at a computer screen. Hopelessly trying to figure out what's the problem with my code.

If you're developing React apps, there is a good chance you're experiencing the same. You're trying to find what is the problem. You're trying to figure out why your app is not working as expected.

Finding a breaking bug in your code can be a painful process. Especially if the codebase is large in size. In situations like these, I wish my code was covered with unit tests properly.

Unit testing is a software testing method by which individual units of source code are tested to determine whether they are fit for use. (source: Wikipedia)

In React applications, there are many parts that can be covered by unit tests. But in this article, we're gonna look at one specific interface to test. I'm gonna show you how to:

  • Write unit tests for React Context
  • Write unit tests for components that use React Context
info

All the code examples used in this article are available on Github.

Before we dive straight into the code examples. Let's talk about the libraries we can use for testing React applications.

React Testing Libraries

In this section, we will cover the libraries and testing tools that can be used in React to make debugging as easy as possible.

This section would cover the following libraries:

  • Jest
  • Enzyme
  • React Testing Library
  • React TestUtils

Jest

Jest is an open-source testing library that can be used to test Javascript code. It was created to provide a better developer experience and is maintained by Facebook.

Jest is a simple and intuitive testing framework that Provides:

  • Interactive watch mode
  • Easy mocking
  • Snapshot testing

Jest is a delightful JavaScript Testing Framework with a focus on simplicity. It works with projects using: Babel, TypeScript, Node, React, Angular, Vue and more! (source: Jest)

You can think of Jest as a tool for test management. With Jest, we can define test cases, group them in the test suites, execute them, and see the test report.

Enzyme

Enzyme is a JavaScript-based testing library for React. The library was created by Airbnb to make their test suite run faster. Enzyme is designed to work with Jest.

The library has many useful features like:

  • Snapshot testing
  • Shallow rendering
  • Flexible asynchronous testing

It also has an elegant API that makes writing tests a breeze.

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. (source: Enzyme)

Enzyme's goal is to make it easier to write tests by providing tools for common tasks like simulating events, querying components, comparing DOM nodes, and mocking dependencies.

React Testing Library

React Testing Library was developed by Facebook and it can be used as an alternative to Enzyme.

The React Testing Library is a very lightweight solution for testing React components. It provides light utility functions on top of react-dom and react-dom/test-utils, in a way that encourages better testing practices. (source: Testing Library)

It's a part of the Testing Library Package which contains all kinds of utilities to make testing easier and more accessible.

We touched the surface of some libraries we're gonna use in the next examples. And now it's time to start coding.

Theme Context Example

Before we dive into the testing part, let me introduce the simple application we need to write tests for. It consists of 2 parts:

  • Theme Provider
  • Simple Button
info

Full source code is available on Github.

The entry point of our application is App component.

App.jsx
import { ThemeProvider } from "./contexts/Theme";
import Button from "./components/Button";

const App = () => {
  return (
    <ThemeProvider>
      <Button>Click me!</Button>
    </ThemeProvider>
  );
};

export default App;

To know what kind of theme is currently being used, we add ThemeProvider.

ThemeProvider.jsx
import { useState } from "react";

import ThemeContext from "./ThemeContext";

const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState("light");

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

export default ThemeProvider;

Lastly, we have the button that gets a className based on a theme value.

Button.jsx
import { useContext } from "react";

import { ThemeContext } from "../../contexts/Theme";

const Button = ({ children, onClick }) => {
  const { theme } = useContext(ThemeContext);

  return (
    <button className={`btn-${theme}`} onClick={onClick}>
      {children}
    </button>
  );
};

export default Button;
note

It's a very simple application, but it will be enough for testing purposes.

How to Test React Context Provider

The first thing we need to test is the Context Provider. And this is where most of the developers get stuck.

It's nearly impossible to test Context Provider without Context Consumer.

But the solution to this problem is fairly simple. All we need to do is implement a test component that will be used in our unit tests.

note

The testing component should be used only by the unit tests. Not in the actual application.

For the testing part, we only need to test 2 things:

  • Is the default theme set properly?
  • Is it possible to change the theme?

With this information in mind, let's implement the unit tests.

ThemeProvider.test.js
import { useContext } from "react";
import { render, screen, fireEvent } from "@testing-library/react";

import ThemeProvider from "./ThemeProvider";
import ThemeContext from "./ThemeContext";

/**
 * This components is for the testing purposes only
 */
const TestComponent = () => {
  const { theme, setTheme } = useContext(ThemeContext);

  const setDarkTheme = () => setTheme("dark");

  return (
    <button data-testid="test-button" className={theme} onClick={setDarkTheme}>
      Test Button
    </button>
  );
};

/**
 * Helper function to render ThemeProvider
 */
const renderTestComponent = () =>
  render(
    <ThemeProvider>
      <TestComponent />
    </ThemeProvider>
  );

describe("ThemeProvider", () => {
  it("sets light theme as default", () => {
    renderTestComponent();

    // classname of <button> should be "light"
    expect(screen.getByTestId("test-button").className).toEqual("light");
  });

  it("changes theme", () => {
    renderTestComponent();

    // Clicking on button will change theme to dark
    fireEvent.click(screen.getByTestId("test-button"));

    // classname of <button> should be "dark"
    expect(screen.getByTestId("test-button").className).toEqual("dark");
  });
});

This was a very simple example that showed how to test React Context Provider. But even for a more sophisticated context, the approach is still going to be the same.

All you need to do is write a test component that will act as a Context consumer. And then test all the functionalities that the given Context has to offer.

How to Mock React Context

In the previous example, we tested if Context Provider works as it should. In this section, we're going to look at the other side of the equation.

In this section, we're gonna write unit tests for Consumer of a Content. Our example app only has 1 component that uses ThemeContext. It's the Button component.

The problem we're facing here is that component we want to test uses Context. Most of the developers including me are against mocking.

The reasoning behind this is that tests should reflect how your application behaves, and the way it's being used. If we ignore the Context while writing unit tests, our unit tests might not be effective enough.

warning

When you start thinking about the testing approach, you might be tempted to mock Context at first. But this is actually wrong. Instead, you should wrap tested components with the actual Context Provider.

To test the button, we need to check the following:

  • Is button rendering?
  • Does buttonhave correctclassName?

Let's take this into account and implement the unit tests.

Button.test.js
import { render, screen } from "@testing-library/react";

import { ThemeProvider } from "../../contexts/Theme";
import Button from "./Button";

const renderButton = () =>
  render(
    <ThemeProvider>
      <Button>Test Button</Button>
    </ThemeProvider>
  );

describe("Button", () => {
  it("renders correctly", () => {
    renderButton();
  });

  it("has correct className", () => {
    renderButton();
    expect(screen.getByText("Test Button").className).toEqual("btn-light");
  });
});
note

You probably noticed the similarity between both unit tests. But in reality, they test 2 different things.

Concluding Thoughts

Testing is an integral part of any application. And if you want to make your React app more robust and error-prone, you should have decent test coverage.

In React apps, there are usually a lot of modules and moving parts. Each one of them requires a different testing approach.

In this article, we looked at the testing React Context API.

tip

Context API is gaining more and more popularity. As a React developer, it's important to know how to use it properly, but also how to test it properly.

In the article, we demonstrated how to:

  • Write unit tests for Context API
  • Write unit tests for components that use Context

I also briefly showed some of the tools that can be used for testing React applications. And explained the differences between them.

With all the information you gained, you can easily start writing unit tests in your React project.