Yomi Eluwande JavaScript developer. Wannabe designer and Chief Procrastinator at Selar.co and worklogs.co.

Guide to testing React Hooks

6 min read 1899

react hooks testing enzyme jest

Editor’s note: This article was last updated on 15 January 2021.

The 16.8.0 version release of React meant a stable release of the React Hooks feature. React Hooks was introduced in 2018 and got favorable reviews from the React ecosystem. It’s essentially a way to create components with features, like state, without the need for class components.

Testing React Hooks Example

What is React Hooks?

The Hooks feature is a welcome change as it solves many of the problems React devs have faced over the years. One of those problems is the case of React not having support for reusable state logic between class components. This can sometimes lead to huge components, duplicated logic in the constructor and lifecycle methods.

Inevitably, this forces us to use some complex patterns such as render props and higher order components and that can lead to complex codebases.

Hooks aim to solve all of these by enabling you to write reusable components with access to state, lifecycle methods, refs e.t.c.

The various types of React Hooks

Below are some of the major Hooks that will be used generally in your React apps:

  • useState :  allows us to write pure functions with state in them
  • useEffect : lets us perform side effects. Side effects can be API calls, updating the DOM, subscribing to event listeners
  • useContext :  allows us to write pure functions with context in them
  • useRef : allows us to write pure functions that return a mutable ref object

The other Hooks that can be used in your React apps for specific edge cases include:

  • useReducer : an alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method. It is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one
  • useMemo : useMemo is used to return a memoized value
  • useCallback :  this hook is used to return a memoized callback
  • useImperativeMethods :  useImperativeMethods customizes the instance value that is exposed to parent components when using ref
  • useMutationEffects :  the useMutationEffect is similar to the useEffect Hook in the sense that it allows you to perform DOM mutations
  • useLayoutEffect : the useLayoutEffect hook is used to read layout from the DOM and synchronously re-render

How to build a React app using React Hooks

Before we go on to see how to write tests for React Hooks, let’s see how to build a React app using Hooks. We’ll be building an app that shows the 2018 F1 races and the winners for each year.

The whole app can be seen and interacted with at CodeSandbox.

We made a custom demo for .
No really. Click here to check it out.

React Hooks Example

React Hooks Example by yomete using react, react-awesome-styled-grid, react-dom, react-scripts, styled-components

In the app above, we’re using the useState and useEffect Hooks. If you navigate to the index.js file, in the App function, you’ll see an instance where useState is used.

// Set the list of races to an empty array
let [races, setRaces] = useState([]);
// Set the winner for a particular year
let [winner, setWinner] = useState("");

useState returns a pair of values, that is the current state value and a function that lets you update it. It can be initialized with any type of value (string, array e.t.c) as opposed to state in classes where it had to be an object.

The other Hook that’s in use here is the useEffect Hook. The useEffect Hook adds the ability to perform side effects from a function component. It essentially allows you to perform operations you’d usually carry out in the componentDidMount, componentDidUpdate, and componentWillUnmount lifecycles.

// On initial render of component, fetch data from API.
useEffect(() => {
    .then(response => response.json())
    .then(data => {
    .then(response => response.json())
    .then(data => {
      let raceWinner = data.MRData.StandingsTable.StandingsLists[0].DriverStandings[0].Driver.familyName + " " + data.MRData.StandingsTable.StandingsLists[0].DriverStandings[0].Driver.givenName;
}, []);

In the app, we’re using the useEffect Hook to make API calls and fetch the F1 races data and then using the setRaces and setWinner functions to set their respective values into the state.

That’s just an example of how Hooks can be used in combination to build an app. We use the useEffect Hook to fetch data from some source and the useState to set the data gotten into a state.

Testing React Hooks with Jest or Enzyme

What are Jest and Enzyme?

Jest and Enzyme are tools used for testing React apps. Jest is a JavaScript testing framework used to test JavaScript apps and Enzyme is a JavaScript testing utility for React that makes it easier to assert, manipulate, and traverse your React Components’ output.

They are probably the go-to testing tools for React, so we’ll see if they can be used to test React Hooks. To do that, I’ve created an app on CodeSandbox that we’ll use for our test suites. You can follow along by forking the app on CodeSandbox.

Navigate to the __tests__ folder to see the hooktest.js file that contains the test suite.

import React from "react";
import ReactDOM from "react-dom";
import App from "../index";
it("renders without crashing", () => {
  const div = document.createElement("div");
  ReactDOM.render(<App />, div);

We’ll first write a test to see if the app renders without crashing.

testing react hooks using jest and enzyme

Next up, we’ll try using the Enzyme testing library to test React Hooks. To use Enzyme we’ll need to install the following dependencies to the CodeSandbox app:

Testing React Hooks

Testing React Hooks by yomete using enzyme, enzyme-adapter-react-16, react, react-dom, react-scripts

Navigate to the __tests__ folder to see the hooktest.js file that contains the test suite.

In the hooktest.js file, an additional test block is added. We are testing using the shallow method imported from Enzyme. The shallow method or rendering is used to test components as a unit. It is a simulated render of a component tree that does not require a DOM.

We get the error below when we try to test using Enzyme.

react hooks testing jest enzyme invalid

Hooks can only be called inside the body of a function component. (https://fb.me/react-invalid-hook-call)

The error above means that Hooks are not yet supported in Enzyme as seen in this issue here.

As a result, we cannot use Enzyme to carry out component tests for React Hooks. So what can be used?

Introducing react-testing-library

react-testing-library is a very light-weight solution for testing React components. It extends upon react-dom and react-dom/test-utils to provide light utility functions. It encourages you to write tests that closely resemble how your react components are used.

Let’s see an example of writing tests for Hooks using react-testing-library.

Testing React Hooks

Testing React Hooks by yomete using enzyme, enzyme-adapter-react-16, react, react-dom, react-scripts

In the app above, three types of Hooks are in use, useState, useEffect, useRef, and we’ll be writing tests for all of them.

In addition to the useState example in which we’re incrementing and decrementing a count, we’ve also added two more examples.

For the useRef Hook implementation, we’re essentially creating a ref instance using useRef and setting it to an input field and that would mean that the input’s value can now be accessible through the ref.

The useEffect Hook implementation is essentially setting the value of the name state to the localStorage.

Let’s go ahead and write tests for all of the implementation above. We’ll be writing the test for the following:

  • The initial count state is 0
  • The increment and decrement buttons work
  • Submitting a name via the input field changes the value of the name state
  • The name state is saved in the localStorage

Navigate to the __tests__ folder to see the hooktest.js file that contains the test suite and the import line of code below.

// hooktest.js
import { render, fireEvent, getByTestId} from "react-testing-library";
  • render — this will help render our component. It renders into a container which is appended to document.body
  • getByTestId — this fetches a DOM element by data-testid
  • fireEvent— this is used to “fire” DOM events. It attaches an event handler on the document and handles some DOM events via event delegation e.g. clicking on a button
  • rerender — this is used to simulate a page reload

Next, add the test suite below in the hooktest.js file.

// hooktest.js

it("App loads with initial state of 0", () => {
  const { container } = render(<App />);
  const countValue = getByTestId(container, "countvalue");

The test checks that if the initial count state is set to 0 by first fetching the element with the getByTestId helper. It then checks if the content is 0 using the expect() and toBe() functions.

react hooks testing jest enzyme getbytestID

Next, we’ll write the test to see if the increment and decrement buttons work.

// hooktest.js

it("Increment and decrement buttons work", () => {
  const { container } = render(<App />);
  const countValue = getByTestId(container, "countvalue");
  const increment = getByTestId(container, "incrementButton");
  const decrement = getByTestId(container, "decrementButton");

In the test above, The test checks that if the onButton is clicked on, the state is set to 1 and when the offButton is clicked on, the state is set to 1.

react hooks testing jest enzyme onbotton offbutton

For the next step, we’ll write a test to assert if submitting a name via the input field actually changes the value of the name state and that it’s successfully saved to the localStorage.

// hooktest.js

it("Submitting a name via the input field changes the name state value", () => {
  const { container, rerender } = render(<App />);
  const nameValue = getByTestId(container, "namevalue");
  const inputName = getByTestId(container, "inputName");
  const submitButton = getByTestId(container, "submitRefButton");
  const newName = "Ben";
  fireEvent.change(inputName, { target: { value: newName } });
  rerender(<App />);

In the test assertion above, the fireEvent.change method is used to enter a value into the input field, after which the submit button is clicked on.

The test then checks if the value of the ref after the button was clicked is equal to the newName. Finally, using the rerender method, a reload of the app is simulated and there’s a check to see if name set previously was stored to the localStorage.

react hooks testing jest enzyme rerender.png

Implementing Redux in your app? Track Redux state and actions with LogRocket

Debugging React applications can be difficult, especially when there is complex state. If you’re interested in monitoring and tracking Redux state for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps – .


In this article, we’ve seen how to write tests for React Hooks and React components using the react-testing-library. We also went through a short primer on how to use React Hooks. Want to learn more? Check out this post on how to avoid common mistakes with React hooks.

If you have any questions or comments, you can share them below.

Yomi Eluwande JavaScript developer. Wannabe designer and Chief Procrastinator at Selar.co and worklogs.co.

9 Replies to “Guide to testing React Hooks”

  1. Its not about testing react hooks, but react component using hooks. :disappointed:

  2. good article, concise summary on the basics & tooling for react hook-component testing. thanks! how do you stub HTTP requests? for example, is there a way to intercept the fetch requests going into the first Formula 1 app on mount use effect hook?

  3. It is just for testing simple react hooks function that you can get from anywhere. You haven’t added Testing for async hook component. :disappointed:

  4. Nice Article! Can you please tell if there is any way to override the default value of the useState(0). For example if I initially want to render the counter using the value 1 instead of 0?

  5. This article and the react addition of hooks makes me want to throw up. If you add state to a pure function, *that function is no longer pure*!! If you add effects to the function, *it is not a pure function!!!*

    The reason this is hard to test is *because* the functions are no longer pure once you do this! There’s a huge amount of context that has to exist for that function to work, none of which is a passed in argument! You can’t have your cake and eat it too.

Leave a Reply