For Developers

How to Conduct Testing Using React Hooks?

How To Conduct Testing Using React Hooks

Finding the right tool is one of the biggest challenges when you want to test React Hooks. You might know how to use the hooks but testing is a different aspect altogether. But with React Hooks, it is now possible to test your apps faster and easier.

This article details about the basics of React Hooks and other types of Hooks and you will also get to learn how to conduct tests with it.

What are React Hooks?

Hooks in React are the new features introduced in React version 16.8. They allow you to use functionalities of React class like state and other features without writing the actual class. Hooks, in other words, are the features that "hook into" the React component state and application lifecycle feature. But you cannot use them inside the classes.

Rules for using Hooks

Following are the 3 rules for using Hooks in your application.

  • You can call Hooks only inside React function components.
  • You can call Hooks only at the top level of a component.
  • You can not use Hooks in conditional statements. (ex. if-else, looping statement).

Types of Hooks

React provides some predefined Hooks. They are divided into 3 possible categories. They are as follows.

Types of React Hooks.webp

Basic Hooks

  • useState()
  • useEffect()
  • useContext()

Additional Hooks

  • useReducer()
  • useMemo()
  • useCallback()
  • useImperativeHandle()

Apart from these predefined Hooks, developers can also use another type of Hooks named “Custom Hooks”. There are JavaScript functions as well whose name starts with the prefix “use” and they can call other Hooks. The advantage of React custom Hooks is that it doesn't need to have any specific structure similar to any predefined functions. We can decide several arguments that get passed through this. Following is a simple example of a Hook.

import React, { useState } from 'react';
    function Example() {
      // variable declaration, which we'll call "counter"
      const [counter, setCount] = useState(0);
    return (
        <div>
          <p>You clicked {counter} times</p>
          <button onClick={() => setCount(counter + 1)}>
            Click here
          </button>
        </div>
      );
    }

The above mentioned example is of the counter which counts clicks when a button is pressed. It is a simple code that can be executed without class declaration.

How do you test a Hook in React?

Testing is one of the most critical aspects of any product or application. It is important for companies to test their products before launching them in market. Every good developer knows the importance of quality code with minimum error.

The testing of the Hook is done similar to any other function. In order to run tests, it's important to have a good testing framework. Following are some testing libraries most developers recommend.

Jest

It is one of the most popular testing libraries React developer uses for React components. The Jest team from Facebook built this library, and a few companies like Uber and Airbnb uses it. The React community recommended this as the first choice of the testing library.

It works with different JavaScript projects from create-react-app to Vue and even with Babel. Jest's UI snapshot testing is one of the most powerful and famous techniques. This library can also perform parallel testing based on predefined processes and optional priorities.

Enzyme

The team at Airbnb designed a JavaScript testing library called Enzyme for React component testing. With the help of this library, you can test a particular component one level deeper, thanks to its shallow rendering feature. The Enzyme also allows you to perform DOM rendering and test Hooks.

React Hooks testing library

This library was built and is maintained by 35+ React community members. It is known as a beginner-friendly testing library, particularly for React Hooks. It provides great documentation so that anyone can easily follow it to test their Hooks. It also comes with additional function tools.

How do you test React Hooks with Jest And Enzymes?

Every developer wants bug-free code to write tests. And testing React Hooks are not different than testing React applications. Here is the sample code of React Hooks tested with the Enzyme framework.

import React, { useState, useRef } from "react";
const Todo = () => {
  const [todos, setTodos] = useState([
    { id: 1, item: "Bug Fixing" },
    { id: 2, item: "Throwing out the trash" }
  ]);
  const todoRef = useRef();
  const removeTodo = id => {
    setTodos(todos.filter(todo => todo.id !== id));
  };
  const addTodo = data => {
    let id = todos.length + 1;
    setTodos([
      ...todos,
      {
        id,
        item: data
      }
    ]);
  };
  const handleNewTodo = e => {
    e.preventDefault();
    const item = todoRef.current;
    addTodo(item.value);
    item.value = "";
  };
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-6">
          <h2>Add Todo</h2>
        </div>
      </div>
      <form>
        <div className="row">
          <div className="col-md-6">
            <input
              type="text"
              autoFocus
              ref={todoRef}
              placeholder="Enter your task"
              className="form-control"
              data-testid="input"
            />
          </div>
        </div>
        <div className="row">
          <div className="col-md-6">
            <button
              type="Submit"
              onClick={handleNewTodo}
              className="btn btn-primary"
            >
              Add Task
            </button>
          </div>
        </div>
      </form>
      <div className="row todo-list">
        <div className="col-md-6">
          <h3>Lists</h3>
          {!todos.length ? (
            <div className="no-task">No task!</div>
          ) : (
            <ul data-testid="todos">
              {todos.map(todo => {
                return (
                  <li key={todo.id}>
                    <div>
                      <span>{todo.item}</span>
                      <button
                        className="btn btn-danger"
                        data-testid="delete-button"
                        onClick={() => removeTodo(todo.id)}
                      >
                        X
                      </button>
                    </div>
                  </li>
                );
              })}
            </ul>
          )}
        </div>
      </div>
    </div>
  );
};
export default Todo;

Testing with Enzyme

Before starting the testing process, we need to install the library. To do so, open your terminal and enter the following command.

npm install --save-dev enzyme enzyme-adapter-16

Move toward the src directory and create a file with the name setupTests.js. In this file, you can configure our Enzyme’s adapter. Enter the code given below in setupTests.js

import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";
Enzyme.configure({ adapter: new Adapter() });

Now, let's get started with writing a test case. Here we are going to test two things:

  • The component renders
  • And whether the initial to-dos is displayed when it renders

To do so, move to the src directory and create a folder with the name __tests__. Now create a file with Todo.test.js where you’ll write tests for the Todo component’s tests.

After this, we are going to import the packages we needed here and create a described block where we’ll fill in our tests.

import React from "react";
import { shallow, mount } from "enzyme";
import Todo from "../Todo";

describe("Todo", () => {
  // Tests go here
});

Lets start with writing test.

#Test 1: Component renders

For this testing, we are going to use a shallow render. This technique allows us to check if the render method component is called. This will confirm that we need the component renders.

it("renders", () => {
  shallow(<Todo />);
});

#Test 2: Displaying initial to-dos

In this test, we will use the Mount method. This method allows us to go deeper with components as compared to shallow tests. Here we can check the to-do item length.

it("displays initial to-dos", () => {
  const wrapper = mount(<Todo />);
  expect(wrapper.find("li")).toHaveLength(2);
});

Testing with Jest

Here is the sample code that we are going to use while testing React Hooks with Jest.

import React, { Component } from 'react';
import axios from 'axios';
import Profile from './Profile';

class App extends Component {
  state = {
    users: [],
    isLoading: true,
    error: null
  }

  getUsers() {
    axios
      .get("https://randomuser.me/api/?results=6")
      .then(response =>
        response.data.results.map(user => ({
          name: `${user.name.first} ${user.name.last}`,
          username: `${user.login.username}`,
          email: `${user.email}`,
          image: `${user.picture.thumbnail}`
        }))
      )
      .then(users => {
        this.setState({
          users,
          isLoading: false
        });
      })
      .catch(error => this.setState({ error, isLoading: false }));
  }

  componentDidMount() {
    this.getUsers();
  }
  render() {
    const { isLoading, users } = this.state;
    return (
      <React.Fragment>
        <div className="container">
          <h2>Random User</h2>
          <div className="row">
            {!isLoading ? (
              users.map(user => {
                return (
                      <Profile key={user.username} user={user} />
                );
              })
            ) : (
              <p>Loading...</p>
            )}
          </div>
        </div>
      </React.Fragment>
    );
  }
}

export default App;

We have already added the dependency while testing with the Enzyme library. So let's start with testing.

#Test 1: Snapshot testing

We can use Snapshot testing to keep track of UI changes in our app. Snapshots are super useful as they capture component code at a moment and it allows us to compare the component in one state vs other possible states.

When you run the test for the first time, the snapshots of components will get saved in a new __snapshots__ folder in the src directory. Here is the code that we need to perform for snapshot testing.

it("renders correctly", () => {
  const wrapper = shallow(
    <App />
  );
  expect(wrapper).toMatchSnapshot();
});

Now run the test with the following command in the terminal.

yarn run test

While the test suite runs, any newly generated snapshot will get saved in the __tests__ folder.

#Test 2: Testing component’s lifecycle methods

React provides Lifecycle method Hooks by default. They get called at a different level of the component's lifespan. This method is easy when we are handling things like API calls.

We will fetch data from API when our component mounts. We can track that while using Jest and the lifecycle method will be summoned. This method will make it possible for us to mock lifecycle methods used in React applications.

it('calls componentDidMount', () => {
  jest.spyOn(App.prototype, 'componentDidMount')
  const wrapper = shallow(<App />)
  expect(App.prototype.componentDidMount.mock.calls.length).toBe(1)
})

That’s it. That's how we can perform testing with both Enzyme and Jest libraries. Although, it doesn't end here. We can perform several tests here too. These are just sample test cases that can help you to understand how testing is done with the use of these libraries.

In this blog, we learned about React Hooks, one of the most exciting features of React-16.6. Here we have also covered how testing with react hooks look like, and the different inbuilt functions and libraries that we can use to perform the testing

We can test every function, from the simple user interface to the complex interface. We have used Enzyme with Jest library. These libraries are very easy to use even inside the app development process.

Author

  • Author

    Sanskriti Singh

    Sanskriti is a tech writer and a freelance data scientist. She has rich experience into writing technical content and also finds interest in writing content related to mental health, productivity and self improvement.

Frequently Asked Questions

Hooks can help you extract the stateful logic from React components so the tester can test each component independently. With this state, the developer can reuse that component even without changing the component hierarchy. This allows multiple components to use the same Hooks.

Also, with Hooks, you can split the components into smaller pieces of code as per their interrelation. Contrary to splitting the same component can be used in a smaller function based on the lifecycle method.

When you pass the useeffect() function, React remembers that your component should perform a function after render. Once the DOM has been updated, React will call the function you passed. This useeffect() function can get used to performing multiple operations such as fetching data or calling imperative APIs, or even the timer feature.

In every React function, Hooks should get used at the top level. Avoid calling Hooks inside nested functions such as loops and conditions. If you follow this rule, Hooks will always be called in the same order when a component is rendered. Using this technique, React maintains the Hook state across multiple useStates and useEffects.

Following are the React Hooks you can use in your next application.

  1. useState

Using this hook, you can create a state variable in your React function component.

  1. useEffect

By using useEffect, we can perform side effects within function components.

  1. useRef

It enables us to create a reference to a defined element/component when the component mounts.

  1. useCallback

The UseCallback hook improves component performance.

  1. useMemo

This method helps improve performance, just like useCallback, but instead of storing callback results, it stores memory.

  1. useContext

When using React, we want to avoid the problem of creating multiple props for passing down data from one component to another. At that time, we can go for useContext.

  1. useReducer

The useReducer hook manages a state similar to the useState hook, but it relies on the reducer function.

Although you can use Hooks, they are optional and 100% backward-compatible. This indicates that if you add or refactor your classes, you will not experience any breaking changes.

Furthermore, Hooks allows you to use React's local state feature without any need to write a class. In function components, Hooks provide access to React state and lifecycle features.

Apart from that, the use of hooks avoids scenarios such as HOC and renderProps to restructure your application with multiple hierarchies, resulting in a more readable and maintainable code base.

No. If you try to use the useState() Hook inside the condition or after some conditional statements, that can return a value. The program will throw a "React hook 'useState' is called conditionally" error. To solve such an error, try to move all Hooks outside the conditional statements.

View more FAQs
Press

Press

What's up with Turing? Get the latest news about us here.
Blog

Blog

Know more about remote work.
Checkout our blog here.
Contact

Contact

Have any questions?
We'd love to hear from you.

Hire and manage remote developers

Tell us the skills you need and we'll find the best developer for you in days, not weeks.

Hire Developers