Features of testing React application on Jest and React-testing-library

10.06.22, Пт, 00:00, Мск,

Author: ALEXEY NEKLESA, software engineer with over 4 years of experience in the design and development of web applications.

Содержание

Testing is an important aspect of software development, which helps to create fast-in-reaction and high–quality software. Any development team becomes more encouraged when they can trust the previously checked code. The software is becoming more scalable and fault-tolerant. There are fewer errors and problems, including when creating patches and updates. Successful, advanced development teams make tests their end-to-end daily process. In some companies, it is even customary to first develop tests aimed at the expected result, and only then create an application for production and customers (TDD – test-driven development methodology).

In the new post, we will test React applications with the Jest frameworks and the React Testing Library (a popular technology stack when it comes to developing on React). By the way, the official React documentation recommends using this toolkit for test needs.

What is automated testing?

The test code automatically runs the methods of the production code of a commercial application and compares the actual results to the previously expected results (assertions) added to the test. In the case of c React, the way of rendering will be tested, as well as the way the application responds to any user interaction with the interface. There are many different approaches to running tests, we will focus on unit and component tests (complex testing).

What is Jest?

The Jest framework allows developers to write and run tests in JavaScript or TypeScript. The main idea behind this library is simplicity. The main goal of the creators was to make the framework easy for development teams to use. Among other useful features, Jest includes a powerful API with functions for isolated tests, snapshot testing, mock testing, code coverage, and many more.

What is React Testing Library?

React Testing Library — JavaScript library designed specifically for React components testing. The essence of its work lies in the fact that it simulates the interaction of a ‘real’ user with a particular component and checks whether the UI elements of an interface work properly.

Jest vs React Testing Library: why not compare?

React Testing Library is not an alternative to Jest. Each tool has its own features and in a full-fledged project would be used both. Jest detects written tests, runs them, checks whether they were executed successfully or returned an error, and then reports the result to the developer. The framework has ready-made functionality for test kits and test scripts, and for running the tests according to certain conditions. As for the React Testing Library, this library generates DOM events and trees, imitating ordinary users and testing React components. If we run tests without a browser, then we always need a virtual DOM to render the application, interact with user interface elements, and understand whether DOM objects behave exactly as they should in the test environment (whether they change in width when clicking on the div buttons).

The React Testing Library framework is not tied to any specific testing engine. You can use any, although the React Testing Library plus Jest as the tech stack is recommended by many experts and a common choice in professional teams in commercial development. In the ‘create-react-app’ package, Jest and React Testing Library are included by default. ‘react-scripts’, an npm package containing pre-configured settings for Webpack, ESLint, and others, automatically sets up the server to track changes. If a test file is modified, the server automatically compiles and runs it. Server restart is not required. Let's examine the specific test code generated automatically in create-react-app and understand the interaction between Jest and React Testing Library.

import { render, screen } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
  render(<App />);
  const linkElement = screen.getByText(/learn react/i)
  expect(linkElement).toBeInTheDocument();
});

In our code, the test scenario is created using Jest. To render and interact with the virtual DOM in our test program, we import and use the render and screen methods from the React Testing Library framework. If you've performed a ‘clean’ installation of React without additional packages (for example, on Ubuntu), you'll need to manually install Jest and React Testing Library.

Setting up the environment for running and covering the production code with tests

Let's start by installing the required libraries and creating a project that we will test. The easiest way is to initiate a React project using create-react-app. We recommend following the commands and running them in your development environment along with us. However, if you prefer to see the ready-made code immediately, you can clone it (git clone) from the GitHub repository here.

Firstly, let's create a React application:

npx create-react-app react-jest-tutorial

Now let's install the React Testing Library. Here's the console command for that:

Then we install additional frameworks that we will need:

Image:Article_Testing_React_application(3).jpg

Creating the code that we will test

Let's create the simplest application. For this purpose, we'll use an open test API that allows us to display on the front end those who have visited the project page localhost:3000. In the root of your virtual machine or any other development environment you are using, find the folder named react-jest-tutorial. Inside it, there is a src directory. The name speaks for itself. Inside the folder, in the file App.js, replace the content with the following:

Did it work? Here, we connect our API and write the output of the names of those who visited our page. Now, create another file in the same src folder named Utils.js and add a function there to capture the name of the logged-in user:

Run the scripts in the virtual machine console with the command ‘npm start’. If everything is done correctly, you will see a message indicating that the code was successfully compiled.

Image:Article_Testing_React_application(6).jpg

As a result, the open API we used will display a list of visitors at localhost:3000:

Image:Article_Testing_React_application(7).jpg

Jest and React Testing Library: Unit Testing

Unit tests are tests that examine individual methods, classes, variables, modules, software objects, or distinct components. Expected results, just like in other types of testing, are compared to the actual outcomes of how a function or another program element has performed. The most commonly used syntax for tests will have the following structure:

describe('мой компонент или функция', () => {
 test('тест делает следующее', () => {
   // Магия происходит тут
 });
});

The ‘describe’ method outlines the input data and the tested component of your application’s front-end part, while the ‘test’ method describes the test itself or a series of tests. Comparisons between expected and actual results occur in the ‘test’ method using the ‘expect’ function (Jest). After automatic execution, such text will maintain a report of the results. Successful checks will be highlighted in green, and issues that require attention will be highlighted in red.

Let's write a Jest test-like scenario to understand this better:

describe('правда это true, а неправда - false', () => {
 test('правда это true', () => {
   expect(true).toBe(true);
 });

 test('неправда - false', () => {
   expect(false).toBe(false);
 });

This is how we verify the conformity of test input data and the results of the executed functionality of our components.

Remember, we wrote a simple application that displays the names of those who visited our page? Now, we will test the functionality of the formatUserName() method. Create a new file called utils.test.js. Remember that before ‘.test.js’ the title of the test file should also be the exact name of the file containing the code you are currently testing. If you remember, our original file for the production application is called utils.js. So, our function takes a string as input and returns the same string with the addition of the ‘@’ symbol at the beginning. In the test, we will check how the function handles an arbitrary string ‘db’. It should result in ‘@db’. Let’s write a unit test:

With create-react-app, running such tests becomes an easy task. Simply use the console command ‘npm run test -- -t 'utils'’. It is advisable to provide comments in the test code, explaining what is being tested and how. This way, in case of a negative test result, even after a long time, you can quickly understand what happened and why. If your testing environment was set up correctly, running a test like ours manually should immediately show you the result:

Ignore the note about one of the tests being skipped; we deliberately aimed for this effect to demonstrate how different testing chain statuses work. Now let’s see, what happens when some method in the production part of the application behaves incorrectly, and the test fails. Below is an example of another test, for which we modified the file ‘utils.test.js’:

import { formatUserName } from "./utils";

describe('utils', () => {
 test('formatUserName не прибавляет @, когда он уже добавлен', () => {
   expect(formatUserName('@jc')).toBe('@jc');
 });
});

As we can see, after running the new test, the situation turned out to be quite different. We expected that if the name of the visiting user already contains ‘@’, the function would not add a second symbol in the result. In our production function, such a scenario was not considered, so the test led to an error. The ‘expect’ method not only provides us with input information for the test but also gives us maximum details in its report about what exactly happened. Let's add a check for the duplication of ‘@’ in the production code.

That's it. Running the test again shows that the error does not repeat, and our code is now more resilient.

Let's use React Testing Library in combination with Jest to test not just functions, but components!

Testing components is not much different from testing application logic. The difference lies in how we write assertions. Let's create a few component testing cases. The first test will simply verify that the component is being rendered. Go back to the ‘App.test.js’ file (remember, it is generated automatically by the ‘create-react-app’ package). Replace the content we wrote earlier with the following:

Just like when we applied only Jest, we used the ‘describe’ and ‘test’ command blocks. However, this time, we're using the ‘render’ function for an individual component. This test will fail in one case: if there is a compilation error in the application, preventing rendering. Even if the test passes successfully, it won't be a complete test because it lacks comparisons - assertions; it doesn't show much. We created it as an example to illustrate the basis of component testing. Let's add a comparison using the ‘expect’ method to make it a full-fledged test!

This test is better; it checks that the component can render and also looks for an element present in the DOM tree with the text ‘Users:’ between nodes. This text is part of our API-based script demonstrating those who visited the localhost resource. Using the object [screen] in Jest constructs precisely illustrates its integration with the React Testing Library. Without RTL, such an object would not exist. React Testing Library provides numerous useful methods for working with components. Run our test, and... it turns out to be unsuccessful.

The issue is that the asynchronous ‘fetch’ function is still ‘hanging’ when the information about what's on the screen is returned. The test ‘sees’ the ‘Loading users…’ message, not the list of users with the label we needed.

Now the test passes successfully!

What are the best libraries for code testing?

Certainly, the combination of Jest and React Testing Library is not the only alternative. There are numerous tools that can be grouped in various combinations. The developer community using React recommends Jest, which is reflected in the official documentation. Another hint at its effectiveness is the notable companies, whose IT departments use it to ensure the robustness of their applications. Among the giants who have bet on Jest are Airbnb, Meta (recognized as an extremist organization and banned in Russia), and Uber. It is not only effective but also highly convenient, as it is available out of the box in create-react-app. Let's delve into its advantages:

  • Easy to install and configure;
  • Easy to control large test suites thanks to snapshot functionality and snapshot comparisons;
  • Provides methods for running asynchronous tests;
  • Supports mock testing and allows integration of third-party developer libraries in this functionality.

Enzyme as an alternative

This framework is interesting because it has a shallow renderer (including the use of React hooks) and can render the DOM similar to the React Testing Library. It simulates user interaction with the interface at runtime. The library is specifically written to work with React.

React Testing Library: why not Enzyme?

React Testing Librar outperforms competing technologies by coordinating well with UI frameworks like Angular and Vue.

What about Jasmine?

Jasmine has several advantages: it does not require DOM objects to run tests, the library can be used for testing both the front-end and back-end parts of an application, functional asynchronous testing is provided, and ‘asserts’ can be finely customised.

Why not Mocha?

Mocha is a JavaScript testing framework designed for use with React. The library distinguishes itself with very simple asynchronous testing functionality and generators in test scenarios.

Conclusion

Jest and React Testing Library provide excellent capabilities for testing React applications. Whether you choose these libraries or others, the main thing is to test your code in general. Applying the TDD methodology or writing test scripts for each production code may seem inconvenient and inefficient to both development and management teams. However, once you overcome the initial challenges, you'll find that you've reached new horizons of software quality, which will undoubtedly be appreciated by clients!