Testing plays a crucial part in ensuring the quality and reliability of your code. With the right knowledge of testing, its methods, and tools, you can ensure your code matches the requirements and is bug-free.
Testing is a critical aspect of software development, yet it is frequently overlooked and undervalued despite its vital role in ensuring the quality and reliability of applications. Testing is important because it helps identify bugs before a software product is released to its end users.
In this article, we'll cover what you need to know about unit testing, getting started with unit testing in your React applications using Jest, from setting up Jest in your project to writing tests.
Table of Contents
- What Is Unit Testing And Why Is It Important?
- Performing Unit Tests With Jest In React
- Mocking Functions With Jest
- Snapshot Testing With Jest
- Conclusion
What Is Unit Testing And Why Is It Important?
Unit testing is an aspect of the overall testing process in software development. It focuses on individual units of an application such as functions, objects, procedures, and so on. The primary purpose of unit tests is to ensure that each unit performs as expected.
From the definition above, we can deduce that unit testing in React is the testing of individual React components.
Why Is It Important?
Below are some important reasons why we need unit tests.
- Unit tests help to set the standard for coding requirements; writing tests while implementing a feature, for example, gives us an idea of how the implementation should be written.
- It prevents developers push bugs to production as it is always mostly detected in the development phase.
- Unit testing simplifies the debugging process.
- Cost-effective - It is cheaper to fix bugs way before a later stage, let's say after it has been pushed to production.
Performing Unit Tests With Jest In React
Jest is a JavaScript testing framework designed to ensure the correctness of any JavaScript codebase. It allows you to write tests with an approachable, familiar, and feature-rich API that gives you results quickly.
-Jest Core Team
Jest in itself is not a library but a framework, it even has a CLI tool that you can use with the command line. Its official website emphasizes its focus on simplicity and that's due to its zero config setup. You can start using it as soon as you install it.
Setup
- Create a new React app
Open your terminal and create a new React app using the command below
npx create-react-app unit-testing-tutorial
- Install Jest using your favorite package manager
When you use cra(create-react-app)
to create a new React project, it has built-in support for Jest, hence we do not have to install it manually. Hence, the only thing you need to do is install react-test-renderer
for rendering snapshots which will also be covered in this article. You can install it with the command below.
with npm
npm install --save-dev react-test-renderer
with yarn
yarn add --dev react-test-renderer
Otherwise, if you are not using cra
, you have to install it manually with your favorite package manager with the command below.
with npm
npm install --save-dev jest react-test-renderer
with yarn
yarn add --dev jest react-test-renderer
Next, add the following code to your package.json
file
{
"scripts": {
"test": "jest"
Testing A React Component
If you used
cra
to create your React app, aApp.test.js
file would have been created for you by default for testing theApp.js
component. To play around, you can run a sample test with thenpm run test
command on your terminal to see how testing works.
In the following steps, we'll get started with testing our own React component with Jest.
- Step 1: Create a Component
Let's create a component and call it MyFirstTest
, which simply renders hello with the name passed to its prop.
import React from "react";
function MyFirstTest({ name }) {
return (
<h1>Hello {name}!</h1>
)
}
export default MyFirstTest;
- Step 2: Create A Test File
Next, we'll create a test file in the same directory as the component and use the extension .test.js
. The .test.js
extension lets Jest know that this is a test file.
The test will check if our component is rendered with the correct name passed to it as a prop. The unit test for our component MyFirstTest
can then be written as seen below:
import { render } from '@testing-library/react';
import MyFirstTest from './MyFirstTest';
test('renders the component with the correct name passed as prop', () => {
const { getByText } = render(<MyFirstTest name="Peter"/>);
const textElement = getByText(/Hello Peter!/i);
expect(textElement).toBeInTheDocument();
});
We can describe the above test as:
We define a test case with the test function provided by Jest. The function takes a string that explains what the test is going to do as its first argument and the second argument is a callback function that implements the test logic.
Inside the callback function, the first thing we do is render the component we want to test with the render function. The render function returns an object that contains a lot of useful functions that can be used to interact with the rendered component.
From these functions, we utilize the
getByText
function. This function searches through the rendered component to find an element that contains the text "Hello Peter".
NOTE: The /i at the end of the string /Hello Peter!/i was used to indicate case insensitivity. Hence, "/i" matches the string "Hello Peter" regardless of the case. i.e HeLLo PETER will still match.
If the component is appropriately rendered, then the element exists in the virtual DOM. We use the
expect
function which gives us access to many matchers (liketoBeInTheDocument()
) to assert that the element is in the virtual DOM. If it asserts that it's present, the test will pass. Else, the test fails.Run the test with
npm run test
command. Your test result should look like the image below.
Mocking Functions With Jest
When a function is mocked, it means a sample or epitome of that function is created as a replacement for that function for the purpose of testing. Its purpose is to verify its result or behavior in a predictable environment.
Jest provides several ways we can perform mocking including jest.fn()
, which we can use to ensure predictable results. With jest.fn()
we can create a sample(mock function) that can be used to replace the original function for testing purposes. The mock function can then be configured to return specific values or throw new errors. By mocking functions, we can confirm that components are using functions as expected. Let's look at an example of how to mock a function using jest.fn()
in the following steps.
- Create a file
math.js
and copy the following code.
//math.js
export const add = (a, b) => a + b;
- Create another file
add.js
and import theadd
function from themath.js
file and copy the code below. This way we don't have to worry about the implementation of how the addition is done in themath.js
file.
import { add } from './math.js';
export const doAdd = (a, b) => add(a, b);
- Create a test file
add.test.js
and import all exports from theadd.js
file as math. This is so they can be accessible in a math object.
import * as math from './add';
test('adds numbers passed as argument', () => {
const sum = math.doAdd(1, 2);
expect(sum).toBe(3);
});
In the code above, we assign the math.add
function which adds the two parameters passed to it to the variable sum
. We then use the expect
method which gives us access to the toBe()
matcher to assert that the result of the sum is 3.
Your test result should look like this after running the test file
Snapshot Testing With Jest
Snapshot testing is used when you want to catch unexpected changes in your UI. It is a technique used when the output of a test is being compared against a previously captured snapshot of the expected result to ensure consistency. The test passes if the output of the function matches the previously captured snapshot, if it doesn't, the test fails. In the following steps, we'll see how snapshot tests work by testing a button
component.
- Create a component and name it
button.js
and copy the following code
export default function Button() {
return(
<button>
Click Me
</button>
)
}
- Create a test file
button.test.js
and copy the following code.
import React from 'react';
import renderer from 'react-test-renderer';
import Button from './Button';
test('Button should match its snapshot', () => {
const button = renderer.create(<Button />).toJSON();
expect(button).toMatchSnapshot();
});
In the code above:
We import the
renderer
method from thereact-test-renderer
library that we installed earlier. We then use therenderer.create
method to render the component.After rendering, the
toJSON()
method is called to create a plain JavaScript object that represents the component and its children which is then saved to the variablebutton
.We then use the
expect
andtoMatchSnapshot
functions to compare the button with a saved snapshot.On testing the file, a
__snapshot__
folder is automatically created where snapshots are stored for comparison. You can think of it as where your history of snapshots is stored.
Your test result should look like this after you run your test:
If there are any changes later(after the first snapshot was taken), Jest will throw an error and ask if you want to update the snapshot.
As a codebase evolves, Snapshot tests are often used in conjunction with unit tests to ensure that the output of a function remains consistent over time.
Conclusion
In conclusion, we see that unit testing is a critical aspect of software development that helps to ensure the quality and reliability of our code. It is an efficient way to validate that individual units of code work as intended, reducing the chances of us pushing bugs to production. So far, we have learned about Jest, how we can mock a function with Jest, and how to test using snapshots. Jest has a lot of other features that were not mentioned here, be sure to get your hands dirty. With the right understanding and use of these tools, we can make our testing process more effective and save time.