An overview of the
Testing-library is the official unit test library recommended by React. The object is Airbnb’s Enzyme. I tried to explain the characteristics of Testing- Library in terms of a popular system of techniques (problem finding, problem analysis, problem solving) :
-
The designers of Testing- Library saw a problem: Previous Unit tests focused on asserting properties inside components, but developers found this Testing a bit self-deceiving
-
To boost developers’ confidence in their test cases, they came up with the idea that tests that more closely resembled how the software was used would be considered more reliable
-
They then designed a bunch of apis to simulate how users (developers) would find or manipulate the DOM
OK, the tet-out -library provides a series of anthropomorphic apis to help us test UI components. For how to personify, see the example below.
The installation
This article uses react as an example. If you use create-react-app, you can skip this chapter. If not, the following two dependencies need to be installed:
yarn add -D @testing-library/react @testing-library/user-event
Copy the code
For the record, testing-library is a library, not a framework. While the Framework follows the Hollywood principle (Don’t call me, I’ll call you), libraries typically require active import methods. Testing-library implements the integration of all major testing frameworks. In this article, we use the React official Jest+testing-library combination, so we also need to install @testing-library/jest-dom:
yarn add - D @testing-library/jest-dom
Copy the code
For convenience, a startup file is usually added to jest to import @testing-library/jest-dom; Of course, you can omit this step, but it is a bit troublesome to add the following sentence to each test file.
// setupTests.js
import "@testing-library/jest-dom";
Copy the code
// package.json
{
"jest": {
"setupFilesAfterEnv": ["setupTests.js"]}}Copy the code
Get started
Once the installation is complete, we’ll start our first test case. Write a Hello World component:
// Title.js
import React from "react";
export const Title = () = > <h1>Hello World</h1>;
Copy the code
The React Testing Library (RTL) uses a method called Render to render the React component (VUE, Angular, and Svelte frameworks use the same Testing Library) :
// Title.test.js
import React from "react";
import { Title } from "./Title";
+ import { render } from "@testing-library/react";
describe("Title", () => {
test("debug Title", () => {
+ render( );
});
});
Copy the code
If beginners want to see the result of render, try screen.debug() :
// Title.test.js
import { render, screen } from "@testing-library/react";
describe("Title".() = > {
test("debug Title".() = > {
render(<Title />);
screen.debug();
});
});
Copy the code
When I run Jest, the console outputs the following: an HTML document. The React component’s DOM is wrapped in the render function’s default container, .
<body>
<div>
<h1>Hello World</h1>
</div>
</body>
Copy the code
Normally, we write test cases without directly printing the render DOM, but keep in mind that all test methods are based on the render results of the render method.
Select elements
Of course, simply rendering a Document using library methods is not a test case; We need at least one assertion: for example, that an element will appear in a rendered Document. We add the following two lines of code after Render:
// Title.test.js
test("getByText of Title", () => {
render(<Title />);
+ const $e = screen.getByText("Hello World");
+ expect($e).toBeInTheDocument();
});
Copy the code
Explain:
- using
getByText
Find a contain textHello World
An element of - Assert that this element is in Document
Run jEST again and the test passes; A very basic test case is complete.
2. Skipped over or skipped over. 2. Skipped over or skipped over. 3. Skipped over or skipped overCopy the code
In this case, we used getByText — use text to view the target element. Does that sound like a scene where you go to inspect some text in the browser and find the target element? This is what makes the RTL API unique: it simulates the use case operations of the developer.
In addition, there are several apis that can also help us view elements. But did you notice that there’s no selector here? ! This is the anthropomorphic feature mentioned above: you don’t need to know what ID or class is being used inside the component; You only need to be vaguely aware of an HTML tag to start testing.
- getByRole(‘button’):
<button>click me</button>
- getByLabelText(‘search’):
<label for="search" />
- getByPlaceholderText(‘Search’):
<input placeholder="Search" />
- getByAltText(‘profile):
<img alt="profile" />
- getByDisplayValue(‘Javascript):
<input value="JavaScript" />
The search variables
getByText
Let’s move on to getByText. We used getByText(‘Hello World’), which is a full-text match search, and it actually supports regular expressions. The following case can also pass; As long as you know the variable of the text, getByText can help you find the target element.
test("getByText by regular expression".() = > {
render(<Title />);
const $e = screen.getByText(/Hello/);
expect($e).toBeInTheDocument();
});
Copy the code
queryByText
In addition to getByText, RTL provides a similar method called queryByText. If getByText fails to find an element, it throws an exception and the test case breaks. QueryByText returns NULL in this case, so queryBy is often used to assert that an element does not exist in a Document. This kind of test is quite common, such as passing a parameter to a component to make it hide an element.
test("search queryByText of Title".() = > {
render(<Title />);
const $e = screen.queryByText(/Onion/);
expect($e).toBeNull();
});
Copy the code
findByText
A third, similar method called findByText is an asynchronous function; For testing components that require asynchronous rendering:
// AsyncTitle.js
import React, { useState, useEffect } from "react";
export const AsyncTitle = () = > {
const [user, setUser] = useState(null);
useEffect(() = > {
const loadUser = async() = > {await "simulate a promise";
setUser("Onion");
};
loadUser();
});
return user && <h1>Hello {user}</h1>;
};
Copy the code
// AsyncTitle.test.js
test("findByText of AsyncTitle".async () => {
render(<AsyncTitle />);
const $e = await screen.findByText(/Hello/);
expect($e).toBeInTheDocument();
});
Copy the code
Multielement selection
All of the above are selecting the element that appears first. Of course, there are all of them, and the apis are similar to the above three sets — getAllBy, queryAllBy, findAllBy. Tagtext is an empty PlaceholderText function. All search returns an array, and the test is similar, but with a loop, I won’t expand it here.
The event
Then we’ll talk about how to test UI events. We don’t look at the source code, just look at the component UI effect.
The function of this component is simple: enter text in the input box, it will display the corresponding text. If you were tester, how would you test? I think there are three specific steps:
- Select the input field
- Enter text
- Verify that the entered text is displayed
Unit test:
// InputTitle.test.js
import userEvent from "@testing-library/user-event";
test("type InputTitle".() = > {
render(<InputTitle />);
// Step 1
const $input = screen.getByRole("textbox");
// Step 2
const val = "Hello World";
userEvent.type($input, val);
// Step 3
const $text = screen.getByText(val);
expect($text).toBeInTheDocument();
});
Copy the code
Isn’t that intuitive?
-
Find the input field $input; The role of tag is a textbox. It doesn’t matter, getByRole, the console will tell you)
-
Use userEvent.type to simulate user input
-
Assert that the corresponding input text is already displayed in the Document
Take a look back at the source code for this component; Have you ever felt that you can write UI tests without knowing the implementation?
// InputTitle.js
import React, { useState } from "react";
export const InputTitle = () = > {
const [head, setHead] = useState("");
return (
<div>
<h1>{head}</h1>
<input
type="text"
value={head}
onChange={(e)= > setHead(e.target.value)}
/>
</div>
);
};
Copy the code
Finally, the @testing-library/ user-Event library used above provides a complete set of user actions for RTL: in addition to type, there are also several types of user actions that you can try.
- click(element, eventInit, options)
- dblClick(element, eventInit, options)
- type(element, text, [options])
- upload(element, file, [{ clickInit, changeInit }])
- clear(element)
- selectOptions(element, values)
- deselectOptions(element, values)
- tab({shift, focusTrap})
- hover(element)
- unhover(element)
- paste(element, text, eventInit, options)
- specialChars
summary
Many old front ends don’t write Unit Test because UI tests are too hard to write. This period looked at the RTL introductory case, we have not wavered; Well, front-end testing isn’t that hard to write, is it?
This is the first installment of Testing-Library 101, and the next installment will cover some advanced test cases, such as callbacks, asynchronous updates, and error catching.
- React Testing Library 101 (2)