In computer programming, Unit Testing (English: Unit Testing), also known as module Testing, is a test for the correctness of the program module (the smallest Unit of software design).
A program unit is the smallest testable part of an application. In procedural programming, a unit is a single program, function, procedure, etc. For object-oriented programming, the smallest unit is a method, including methods in a base class (superclass), an abstract class, or a derived class (subclass).
—- from Wikipedia
In layman’s terms, in the front-end world, a unit test is a single module of testing, either a function or a component.
The advantages of unit testing are as follows:
- Unit testing is an act of validation
- Unit testing is a design activity
- Unit testing is the act of writing documentation
- Unit testing is a regression testing activity
At present, there are many unit testing frameworks, this article will take JEST as an example to discuss.
First experience of Jest
What is the jest
Jest is an elegant and concise JS testing framework that supports Babel, TS, Node, React, Angular, vue, and many other frameworks.
He has the following advantages:
- Safety fast
- Complete code coverage with only one command
- Easy to simulate
- A friendly error
- Strong ecology
- Complete documentation
Environment to prepare
- Initialize a project:
npm init
Copy the code
- Install the jest
yarn add jest -D
Copy the code
hello world
- Add the files you want to test
touch t.js
Copy the code
As follows:
function sum(a, b) {
return a + b;
}
module.exports = sum;
Copy the code
- Creating a Test file
touch t.test.js
Copy the code
The test script is as follows:
const sum = require("./index");
test("adds 1 + 2 to equal 3".() = > {
expect( sum(1.2) ).toBe(3);
})
Copy the code
- Configuration package. Json
{
...,
"scripts": {
"test": "jest"
}
}
Copy the code
- Run NPM run test
Ii. Jest adapter
In the above example, toBe is a matcher, and the simplest
toBe
ToBe is exact equality, that is x === y
toEuqal
Like toBe, he judges equality, but he judges objects
toBeNull
Result to match NULL
toBeUndefined
The result only matches undefined
toBeDefined
In contrast to the toBeUndefined
toBeTruthy
Matching any if statement to true is essentially expecting true
toBeFalsy
Matching any if statement to false is, in effect, an expected result of false
toBeGreaterThan
When matching numbers, expect greater than result > xx
toBeGreaterThanOrEqual
When matching numbers, expect results >= xx
toBeLessThan
When matching numbers, expect less than, that is, result < xx
toBeLessThanOrEqual
When a number is matched, the expected value is less than or equal to result <= xx
toBeCloseTo
Decimal precision problem matching, there is a classic problem: 0.1 + 0.2! == 0.3, but we expect to be equal, and we need to use toBeCloseTo
toMatch
Used when matching a string that is expected to contain another string.
Example:
expect("abc").toMatch("a") // ok
Copy the code
toContain
Array operations, like indexOf, whether an array contains xx.
Example:
expect( [1.2.3] ).toContain(1) // ok
Copy the code
More and more
IO/zh-hans /doc…
Jest asynchronous code test
Testing the code, note that jEST tests are executed synchronously, and if you write an asynchronous task directly, the test expectation will be executed before the asynchronous task. At this point, we need to execute done manually.
1. Asynchronous functions
const axios = require("axios");
function fetchData() {
return axios("http://localhost:3001/");
}
module.exports = fetchData;
Copy the code
2. Test asynchronous functions
const fetchData = require("./t");
test("fetch Data".(done) = > {
fetchData().then((data) = > {
expect(data.data).toBe("ok"); // ok
done();
});
});
Copy the code
Jest Global hook
If we need to do something before we run the test, or after we run the test, we need to do something. Let’s say we need to do something before or after each test case
Well, we can use:
beforeEach
Before each test case, execute
beforeEach(
() = > console.log("I'm doing it before every test case."))Copy the code
afterEach
After each test case, execute
afterEach(
() = > console.log("I do it after every test case."))Copy the code
beforeAll
Execute only once before all cases.
beforeAll(
() = > console.log("Execute once only, before all test cases"))Copy the code
afterAll
Execute after all cases, only once.
afterAll(
() = > console.log("Execute once only, after all test cases"))Copy the code
Mock functions
We use mock functions when we need to mock callback calls.
demo
Code to be tested:
function sum(a, b, callback) {
return callback(a + b);
}
module.exports = sum;
Copy the code
Test code:
const sum = require("./t");
test("call".() = > {
const mockCallback = jest.fn();
sum(1.2, mockCallback);
sum(3.4, mockCallback);
expect(mockCallback.mock.calls[0] [0]).toBe(3);
expect(mockCallback.mock.calls[1] [0]).toBe(7);
});
Copy the code
The jest. Fn method is the primary use of mock functions, and every such method has the mock attribute.
Mockcallback. mock property, which has the following data structure:
{
calls: [[3 ], [ 7]],instances: [ undefined.undefined].invocationCallOrder: [ 1.2].results: [{type: 'return'.value: undefined },
{ type: 'return'.value: undefined}}]Copy the code
React + Jest
create / destroy
Each test needs to render to a DOM, which requires a div to be created before each test. When it’s done, delete it.
import { unmountComponentAtNode } from "react-dom";
let container = null;
beforeAll(() = > {
container = document.createElement("div");
document.body.appendChild(container);
});
afterAll(() = > {
unmountComponentAtNode(container);
container.remove();
container = null;
});
Copy the code
act
It ensures that all updates related to module units are processed and applied to the DOM before any assertions are made
The following is an example:
act(
() = > {
// Render component})// make an assertion
Copy the code
Render unit test
Components to be tested:
import React from "react";
function Main() {
return <div>a</div>;
}
export default Main;
Copy the code
Unit test code:
import React from "react";
import { unmountComponentAtNode, render } from "react-dom";
import { act } from "react-dom/test-utils";
import Main from "./t";
let container = null;
beforeAll(() = > {
container = document.createElement("div");
document.body.appendChild(container);
});
afterAll(() = > {
unmountComponentAtNode(container);
container.remove();
container = null;
});
test("test".() = > {
act(() = > {
render(<Main />, container);
});
expect(container.textContent).toBe("a");
});
Copy the code
The event
DOM event test, you can assert the results, using jest. Fn simulation function.
React component:
import React, { useState } from "react";
function Home(props) {
const [state, setState] = useState(true);
const handleClick = () = >{ setState(! state); props.onChange(! state); };return (
<div>
<button id="btn" onClick={()= > handleClick()}>
{state ? "a" : "b"}
</button>
</div>
);
}
export default Home;
Copy the code
Test code:
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import Home from "./index";
let container = null;
beforeEach(() = > {
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() = > {
unmountComponentAtNode(container);
container.remove();
container = null;
});
it("Update value when clicked".() = > {
const onChange = jest.fn();
act(() = > {
render(<Home onChange={onChange} />, container);
});
const button = document.getElementById("btn");
expect(button.innerHTML).toBe("a");
act(() = > {
button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
expect(onChange).toHaveBeenCalledTimes(1);
expect(button.innerHTML).toBe("b");
});
Copy the code
A snapshot of the test
Jest will save the last snapshot and give diff hints when our component changes.
The original component
import React from "react";
function Home(props) {
return <div>test</div>;
}
export default Home;
Copy the code
Test code:
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import pretty from "pretty";
import Home from "./index";
let container = null;
beforeEach(() = > {
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() = > {
unmountComponentAtNode(container);
container.remove();
container = null;
});
it("Update value when clicked".() = > {
act(() = > {
render(<Home />, container);
});
expect(pretty(container.innerHTML)).toMatchInlineSnapshot(
Test ` "< div > < / div >" `
);
});
Copy the code
Run as follows:
When we change the original component code (changing the test copy to Test 1), we run the result again as follows:
7. Report
After you run the Jest test, a Coverage folder appears in your project, which contains the index.html file. When you open it, you can see the specific test report. As follows:
Eight. Summary
Jest is the unit test runner recommended by React. The React framework itself also uses JEST, which is well supported by the community ecosystem.
React test library, that is really delicious ~~
Code word is not easy, please like more, pay attention to ~ 😽