DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Dan Musembi
Dan Musembi

Posted on

Unit Testing of React Apps using JEST.

Image description

Software testing is just as crucial as development because it reveals whether or not the final product satisfies the stated requirements. It is more efficient, more secure, and cheaper to use a product that has been subjected to extensive testing because of its reliability, security, and high performance.

To create elaborate user interfaces, many developers turn to the widely-used JavaScript package ReactJS. The graph below, which displays the number of downloads over time, should give you some idea of how popular it is.

Image description

These are just some of the many reasons why developers love the React framework.

  • High Performance: In terms of benefits, React JS shines most brightly because of how quickly it executes. It's made feasible by a confluence of factors, including:
    When nodes need to be re-rendered, React employs virtual DOM to do it.
    Bundling and tree shaking are two techniques that reduce the burden on the user's system and are supported by React JS.

  • Quick Rendering: Utilizing virtual DOM and technologies like diffing, and React JS allows for rapid and efficient application development.

  • Ease of Learning: The learning curve for React JS is much lower than that of other front-end libraries and frameworks; as a result, any developer with a foundational understanding of JavaScript can pick up the language and begin creating applications.

  • One-way data-binding: Since React JS uses one-way data binding, you have greater influence over how the application operates.

Table of Contents

  • What is Unit Testing for React Apps? Why is it important?

  • How to perform Unit testing of React Apps using JEST?

  • Mocking Data with Jest

  • Code Coverage using Jest

  • Best practices for testing React Apps with JEST

When developing a React app, what exactly is "unit testing"? So why is this so crucial, exactly?

During unit testing, each subsystem is evaluated independently. Individual React components can be tested as part of unit tests for React applications.

Testing the different components of a React app is where "unit testing" comes in. In addition, it is possible to spot coding mistakes early on, which speeds up the correction process. There are many advantages to doing Unit Testing, but here are a few of the more important ones:

  • Process Becomes Agile: The key benefit of unit testing is its integration into the Agile Testing process. Adding new features to software often necessitates reworking previously implemented sections of code and design. This may require more time and money. However, if you employ unit testing, you will find that the entire operation moves along a lot more quickly and easily.

  • Quality of code: The quality of the code is greatly enhanced by conducting unit tests. Before moving on to integration testing, it aids engineers in locating even the tiniest of unit-level bugs.

  • Facilitates change: When you test each part of the app separately, it's significantly simpler to make changes like refactoring the code or updating the system library.

  • Smooth Debugging: Unit testing greatly reduces the time and effort required for troubleshooting. If a test fails, the code only needs to be debugged if it has been modified since the last time the test passed.

  • Reduction in cost: Finding and fixing defects during production can be quite expensive, but if caught during unit testing, they can be addressed for nearly nothing.

How to Use JEST for Unit Testing in React Apps?

Jest is a JavaScript testing framework that can be easily integrated with React JS and allows developers to conduct tests on JavaScript and TypeScript code.

Step 1: Create a new react app

Let's make a simple react app for testing purposes by running the command below:

Image description

Step 2: Create a component

Let's make a component we'll call "Counter" that lets us add or subtract from a given number with the click of a button.

import React, { useState } from "react";

const Counter = () => {
const [counter, setCounter] = useState(0);

const incrementCounter = () => {
setCounter((prevCounter) => prevCounter + 1);
};

const decrementCounter = () => {
setCounter((prevCounter) => prevCounter - 1);
};

return (
<>
<button data-testid="increment" onClick={incrementCounter}>
+
</button>
<p data-testid="counter">{counter}</p>
<button disabled data-testid="decrement" onClick={decrementCounter}>
-
</button>
</>
);
};

export default Counter;

Enter fullscreen mode Exit fullscreen mode

Here, the important thing to note is the data-testid attributes that will be used to select these elements in the test file.

Step 3: Write a unit test for the react component

Let's look at the overall layout of a test suite before diving into the actual scripting of a unit test:

  • A test is usually written in a test block.

  • Inside the test block, the first thing we do is to render the component that we want to test.

  • Select the elements that we want to interact with

  • Interact with those elements

  • Assert that the results are as expected.

The following code sample demonstrates how to write a unit test for a React component:

import { render, fireEvent, screen } from "@testing-library/react";
import Counter from "../components/Counter";

//test block
test("increments counter", () => {
// render the component on virtual dom
render(<Counter />);

//select the elements you want to interact with
const counter = screen.getByTestId("counter");
const incrementBtn = screen.getByTestId("increment");

//interact with those elements
fireEvent.click(incrementBtn);

//assert the expected result
expect(counter).toHaveTextContent("1");
});
Enter fullscreen mode Exit fullscreen mode

This test can be summed up as:

  • The test block can be written either using test() or it(). Either of the two methods takes two parameters:

    • The first parameter is to name the test. For example, increments counter.
    • The second parameter is a callback function, which describes the actual test.
  • Using the render() method from the react testing library in the above test to render the Counter component in a virtual DOM.

  • The screen property from the react testing library helps select the elements needed to test by the test ids provided earlier.

  • To interact with the button, using the fireEvent property from the react testing library in the test.

  • And finally, it is asserted that the counter element will contain a value β€˜1’.

Step 4: Run the test

Run the test using the following command:

npm run test
Enter fullscreen mode Exit fullscreen mode

Test Result

Image description

Mocking Data with Jest

Let’s create a component and fetch some data using Axios. For that first install Axios using the following command:

npm i axios
Enter fullscreen mode Exit fullscreen mode

Create a new component inside the components folder as given below:

import React, { useState, useEffect } from "react";
import axios from "axios";

const Todos = () => {
const [todoList, setTodoList] = useState(null);

useEffect(() => {
(async () => {
const todos = await axios.get(
"https://jsonplaceholder.typicode.com/todos"
);
setTodoList(todos.data);
})();
}, []);

return todoList ? (
<ul>
{todoList.map((todo, index) => (
<li key={index} data-testid='todo'>{todo.title}</li>
))}
</ul>
) : (
<p>Loading....</p>
);
};

export default Todos;
Enter fullscreen mode Exit fullscreen mode

The above component is simply showing a list of things todos on the browser. Now, one way to test this component is to have the test function make a call to the endpoint of the API and then check if the result is correct.

But there are a couple of issues with this approach:

- It can be expensive to create multiple requests.

- Making requests and getting responses can be a slow operation.

- here is an external dependency in the test i.e. Axios.
Enter fullscreen mode Exit fullscreen mode

So the answer to all of these problems is to make fun of them. The goal of mocking is to keep the code being tested from being affected by outside factors like API calls. To do this, dependencies are replaced with controlled objects that act like dependencies.

For creating a mock with jest, first import Axios using the following command:

import axios from 'axios'
Enter fullscreen mode Exit fullscreen mode

Then mock it using the below command:

jest.mock('axios')
Enter fullscreen mode Exit fullscreen mode

Then create dummy data that has a similar format to the actual result, and return the mocked value:

const Todos = [
{
userId: 3,
id: 3,
title: "todo 3",
completed: false,
},
{
userId: 3,
id: 4,
title: "todo 4",
completed: false,
},
{
userId: 3,
id: 5,
title: "todo 5",
completed: false,
},
];

axios.get.mockResolvedValue({ data: dummyTodos});
Enter fullscreen mode Exit fullscreen mode

Test Result

After running the test, the result obtained can be seen below:

Image description

Code Coverage using Jest

Code Coverage is a way to figure out how much code has been run while the test is being run. It's easy to make a code coverage report with jest. If you're using npm, run the code below to find out how much of the code has been used:

npm test – –coverage

With yarn, run the following command to get the code coverage:

yarn test –coverage

What will happen will be something like this:

Image description

The best ways to use JEST to test React apps:

  • Avoid unnecessary tests: Consider a test where you expect some element to be defined and then expect it to have some property. Now, the last one is no longer needed, because what's the point if the element wasn't even defined?

  • Put business logic into pure functions instead of user interface elements: Think about having a Shopping Cart UI component that doesn't add up the items in the cart. This should be moved to a pure function so it can be tested more easily.

  • Never verify specifics of an implementation: Test implementation details if your test performs something your user doesn't. You may expose a private method for component testing. Avoid this code smell. Refactors often break tests.

Conclusion

When it comes to enhancing the quality of your React apps, unit testing is the simplest solution since it helps in locating flaws and errors in your code. And since less time is spent addressing issues in the later stages of the project, the total cost of development is reduced when code bugs are found early in the SDLC. The result is happier customers and a more reliable clientele.

FOLLOW ME for great content in 2023!βœ…πŸŽ‰ on Twitter and here also.

Top comments (0)

11 Tips That Make You a Better Typescript Programmer

1 Think in {Set}

Type is an everyday concept to programmers, but it’s surprisingly difficult to define it succinctly. I find it helpful to use Set as a conceptual model instead.

#2 Understand declared type and narrowed type

One extremely powerful typescript feature is automatic type narrowing based on control flow. This means a variable has two types associated with it at any specific point of code location: a declaration type and a narrowed type.

#3 Use discriminated union instead of optional fields

...

Read the whole post now!