preface

I find a lot of the front ends are very motivated and interested in implementing business functions. But when it comes to writing test cases, it suddenly feels dull and uninteresting. The reason for this is a lack of awareness of the importance of testing and the benefits it can bring you. The quality and stability of the final application largely determines the success of the project, and the only way to ensure the quality of the software is through testing. So this article can help you break through yourself, to the article good practice master easy to deal with the front-end test wish you zero bugs in the work.

If you find this helpful, give me a thumbs up with your rich little hands. Thank you very much!

Benefits of testing

Not only does testing ensure the quality of your final release, but more importantly, it gives you confidence in the changes you’re making when developing new features or fixing bugs. As long as you have enough test coverage, you don’t have to worry too much about your changes breaking existing features. At the same time, unit testing can help you better organize your code and pursue loose coupling of modules during development. Because if you consciously think about how each piece of your code will be tested, you will consciously isolate modules when you implement your code, which in turn improves the quality of your code. This is why many teams embrace test-driven development.

How to use Jest and React Testing Library for unit Testing

To do a good job, he must sharpen his tools. To start unit testing, the first thing to do is choose your testing framework. This actually consists of two parts:

  1. One is a generic JavaScript testing framework for organizing and running your test cases.
  2. The other is the React testing framework, which renders React components in memory and provides a library of tools to validate the results of the tests.

There are a lot of options for both. For example, generic JS testing frameworks are Mocha, Jasmine, Jest, etc. React test frameworks include Enzyme, Testing Library, etc. If you already have some foundation in front-end development, you’re probably already familiar with some of the frameworks. However, today we use Jest and React Testing Library, simply because they are officially recommended frameworks for React, which means they are reliable and stable. The React project created with create-react-app already includes both frameworks by default and is configured so we can just start writing test cases.

Of course, if you prefer the other option, that’s fine, because I’m going to be talking more about how testing works. Once you understand these principles, you have a grip on how to use a framework, and you can apply the principles to other frameworks.

For the rest, I’ll assume you’ve already created your project using create-react-app, so you don’t have to install and configure it. So let’s look directly at how we can use Jest and the Testing Library for unit Testing.

Use Jest to create unit tests

Jest is a unit testing framework for JavaScript from Facebook. It provides concurrent testing, test coverage, Mock tools, assertion apis, and more, right out of the box with zero configuration.

There are three main things to know about Jest to get started quickly:

  1. Where Jest looks for test files;
  2. How to create a test case and verify the test results with assertions;
  3. How to run tests.

We can understand these three points with a simple example. First, we need to create an add.js file in the SRC directory with the following contents:

export default (a, b) => a + b;
Copy the code

Then, create an add.test.js file in the SRC directory. Because by default, Jest looks for all files in the SRC directory that end with.test.js(ts, JSX, TSX, etc.), as well as files in the Tests folder, and treats them as test files. The contents of the add.test.js file are as follows:


import add from './add';


// Create a test case with the test function
test('renders learn react link'.() = > {
  // Execute the add function to get the result
  const s = add(1.2);
  // Use the Expect function provided by Jest to assert that the result is equal to 3
  expect(s).toBe(3);
});
Copy the code

As described in the code comments, the test function is primarily used to create a test case, and the Expect function asserts the results of the execution. Expect is so powerful, in fact, that in this example, we’ve just shown how to assert that two values are equal. You can refer to the official documentation for the full API.

Then, after the test cases are created, we can run the tests in the project root directory with the command NPX jest –coverage.

The actual result is as follows:

As you can see, our test case ran successfully. Also, we get an automatically generated coverage report by adding the –coverage parameter to the jest command.

With that, we did our first unit test with Jest. However, this is only a Test of pure JavaScript logic. For components like the React application that require a browser environment, you’ll need to introduce the Test Library.

Test the React component using the React Testing Library

To unit test a React component, what kind of runtime environment should be provided to run test cases? In fact, it can be divided into the following three points:

  1. You need a browser runtime environment. This is mainly achieved through jsDOM, an NPM module. It provides a virtual browser environment within the NodeJS environment, including almost all browser apis, such as Document, window, etc., so that your components can run in memory.
  2. You need to be able to parse JSX and the latest JavaScript syntax used in your project by configuring Babel in Jest.
  3. You need to be able to easily render a React componentAnd verify the results. This is exactly what the Testing Library provides.

To help you understand what Testing Library provides, take a look at the contents of the app.test.js file that comes with the project, which uses Testing Library


// Introduce the testing-library tools
import { render, screen } from '@testing-library/react';
// Import the component to be tested
import App from './App';


Create a test case
test('renders learn react link'.() = > {
  // Use the render method to render App components
  render(<App />);
  // Find the DOM element on the page using getByText provided by Screen
  const linkElement = screen.getByText(/learn react/i);
  Asserts that the element should be on the page
  expect(linkElement).toBeInTheDocument();
});
Copy the code

There are three React-related Testing apis provided by the Testing Library:

  1. renderRender a React component in memory.
  2. screen: provides utility methods for getting elements on the screen. Screen.getbytext, for example, is used to get DOM elements from text.
  3. Expect to expandThe Testing Library extends Expect’s capabilities to facilitate assertion judgments on UI elements. The toBeInTheDocument example is used to assert that DOM elements need to exist in the Document.

Although this is only the simplest use of Testing Library, this example will give you an idea of how Testing Library provides unit Testing for the React component. You can refer to the official API documentation for details.

How do I unit test custom Hooks

After introducing the Unit Testing framework Jest and the React Testing Library, you should have a general idea of how to conduct unit tests in React. Which brings us to today’s topic: How to unit test React Hooks.

Unit tests that use Hooks, which are generic functions, are much easier than component tests.

In fact, although I have mentioned several times in previous lessons that we should treat Hooks as ordinary functions, there is one caveat: Hooks are only treated as ordinary functions if they are used in function components. Because the use of Hooks requires a context in which React is run. This is also the principle behind Hooks: Hooks can only be called from function components or custom Hooks.

So, in a unit test environment, it is not feasible to run Hooks separately from function components for unit tests.

By this point, you should be able to understand. To unit test the Hooks again, we use function components.

Such as:


import { useState, useCallback } from 'react';


export default function useCounter() {
  const [count, setCount] = useState(0);
  const increment = useCallback(() = > setCount(c= > c + 1), []);
  const decrement = useCallback(() = > setCount(c= > c - 1), []);


  return { count, increment, decrement };
Copy the code

As you can see, this Hook manages the count variable and provides methods to add or subtract one from it. The test we need to do is to be able to call both methods and check that the count value changes correctly.

An intuitive way to do this is to create a test component that uses the Hook inside the test component. Therefore, the Hook test can be converted to the component test, so the code is as follows:


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


test('useCounter'.() = > {
  // Create a test component that uses all the logic of useCounter
  const WrapperComponent = () = > {
    const { count, increment, decrement } = useCounter();
    return (
      <>
        <button id="btnMinus" onClick={decrement}>-</button>
        <span id="result">{count}</span>
        <button id="btnAdd" onClick={increment}>+</button>
      </>
    );
  };
  
  // Render the test component
  render(<WrapperComponent />);


  // Find the three DOM elements of the page to perform the action and validate the results
  const btnAdd = document.querySelector('#btnAdd');
  const btnMinus = document.querySelector('#btnMinus');
  const result = document.querySelector('#result');


  // Simulate the + 1 button
  fireEvent.click(btnAdd);
  // Verify that the result is 1
  expect(result).toHaveTextContent('1');
  // Simulate clicking the subtract button
  fireEvent.click(btnMinus);
  // Verify that the result is 0
  expect(result).toHaveTextContent('0');
}
Copy the code

As you can see, we can test for Hooks indirectly by creating a test component that uses the Hooks to be tested, and then by testing the functionality of the component.

However, the downside is obvious: we need to write a lot of code that has nothing to do with the Hooks tests themselves. Like these DOM elements on the page.

So, can we manipulate the Hooks API more directly? It’s actually possible. We can expose the return value of the useCounter Hook outside of the function component, and then test code calls these apis directly and verifies the results. The following code demonstrates this approach:


import { render, act } from '@testing-library/react';
import useCounter from './useCounter';


test('useCounter'.() = > {
  const hookResult = {};
  // Create a test component that only runs the Hook without generating any UI
  const WrapperComponent = () = > {
    // Copy the return value of useCounter to an external hookResult object
    Object.assign(hookResult, useCounter());
    return null;
  };
  // Render the test component
  render(<WrapperComponent />);
  
  // Call the increment method of hook
  act(() = > {
    hookResult.increment();
  });
  // The validation result is 1
  expect(hookResult.count).toBe(1);
  // Call the Decrement method of hooks
  act(() = > {
    hookResult.decrement();
  });
  // Verify that the result is 0
  expect(hookResult.count).toBe(0);
});
Copy the code

As you can see from this code, we expose the return value of the Hook outside of the function component, so that we can operate directly on the Hook.

It is important to note here that we use a function called ACT to encapsulate a method call to the Hook return value. This is a test function provided by React that simulates the execution of a real React component. It ensures that the React component life cycle is completed after the act callback is executed, such as useEffect. This allows you to verify the component’s rendering results later.

By manipulating the Hooks API directly above, we simplify the test logic for Hooks, essentially using a container component to test using Hooks. The component in this example does nothing but call the useCounter Hook.

Further, could we make a utility function to test hooks specifically? The answer is yes, and that is exactly what the Testing Library provides with a special test package for React Hooks: @testing-library/ React-hooks. Use it as shown in the following code:


import { renderHook, act } from '@testing-library/react-hooks';
import useCounter from './useCounter';
test('useCounter3'.() = > {
  // Call a Hook using the renderHook API
  const { result } = renderHook(() = > useCounter());
  // The return value of Hook is stored in result.current
  // Call the plus one method
  act(() = > {
    result.current.increment();
  });
  // The validation result is 1
  expect(result.current.count).toBe(1);
  // Call the subtract method
  act(() = > {
    result.current.decrement();
  });
  // Verify that the result is 0
  expect(result.current.count).toBe(0);
});
Copy the code

With the @testing-library/react-hooks package, it is possible to create unit tests for custom hooks more semantically. While the rationale for using this package is to use a component that calls Hooks, you do not need to create extra test components in your test code.

Finally, there is one more question that you should be aware of: Do we need to unit test every Hook?

In summary, only for reusable Hooks do you need separate unit tests.

summary

We learned how to do unit tests in React. To unit test React, you need two things: a generic unit test framework with a virtual browser environment, provided by Jest; The other is support for rendering the React component and Hooks, provided by the React Testing Library. This course starts by thinking about the mechanisms needed for unit tests in the React component and Hooks component, and explains how Jest and the Testing Library provide these capabilities. This will give you a fundamental understanding of how the testing framework works, so that when you’re actually using it, you’ll know where to start.

Strong friends

Like to make some friends like me in the front of the advanced friends, sometimes a person’s road is not easy to go, many people go together is interesting. link

If you find this helpful, give me a thumbs up with your rich little hands. Thank you very much!