download
- Running environment node
npm install jest -D
- Change the command in package.json to
"test" : "jest"
Supported test file formats (naming conventions)
- One of two ways
- He would look for both methods to provide documentation for testing
- You can do it either way
Test file format 1
- Support for two suffixes (actually use one, only two suffixes are provided)
- xxx.spec.js
- xxx.test.js
Test file format 2
- create
__tests__
folder
methods
it
||test
- Assertion, used for testing, it and test
- Two parameters
- The title (string)
- The callback (function)
describe
- Use same as above
- Effect to create a separate scope
expect
- Test values
- Introduce the module to be tested in the above file
/ /.. /index.js
const add = (a, b) = > a + b;
module.exports = add;
//add.spec.js || add.test.js
const add = require(".. /index");
describe("Add method test -index".() = > {
test("1 plus 1 should be equal to 2.".() = > {
const res = add(1.1);
expect(res).toBe(2);
});
});
Copy the code
matcher
A parameter
- toBe
- Object. Is is used to judge, so
We need absolute equality
- toEqual
- Commonly used to check whether reference data types are equal,
Do not compare spatial addresses
, using recursion,- It can also be used to check basic data datatypes
- In the case of a function, different reference space addresses will also cause the alignment to fail
digital
- No data type conversion is performed
- toBeGreaterThanOrEqual
- Greater than or equal to
- toBeGreaterThan
- Less than
- toBeLessThan
- Is greater than
- toBeLessThanOrEqual
- Greater than or equal to
string
- toMatch
- Can be passed
regular
andstring
- To pass strings, include is ok, equivalent to includes, but
The difference is that toMatch does not cast data
An array of
- toContain
- Check to see if it exists in the array
An error
- toThrow
- This parameter is optional
- Do not fill, is to check whether there is an error message
- You can enter a type, such as Error or TypeError, which is a check Error type
- You can fill in a string or re to check the error content
There is no parameter
- toBeNull
- Whether to null
- toBeUndefined
- Whether it is undefined
- toBeDefined
- Not undefined, equal to
not.toBeUndefined
- toBeTruthy
- Same as if judgment, can be understood as
if(true) === toBeTruthy()
- toBeFalsy
- A. if B. if C. if D. if
if(false) === toBeFalsy()
The modifier
- not
- On the contrary,
asynchronous
- Tests for asynchronous methods (no return value)
expect.assertions(1)
Represents the number of times an asynchronous task has been executed (ensuring that asynchronous methods are executed)expect(true).toBoe(true)
Indicates execution (becausetrue === true
So if I go to this line, I’m done.)Note:
In the callback of the test function, use thedone
Variables accept parameters at the end of asynchronous tasksDoing done indicates that we should end the asynchronous task here
!
/ /.. /index.js
const delay = (callback) = > {
setTimeout(() = > {
callback && callback();
}, 1000);
};
module.exports = delay;
//__tests__/index.js
const delay = require(".. /index");
test("Callback executed".(done) = > {
expect.assertions(1);
const callback = () = > {
console.log("I execute.");
expect(true).toBe(true);
done();
};
delay(callback);
});
Copy the code
- Promise of the test
- In this case if
Do not accept unless done is used
(Otherwise an error will be reported)- Finally, you need to implement the promise to
Return (async)
- You can also test with async and await
- You can also use jest’s own
resolves
andrejects
Method to handle promises, used below
const delayPromise = (callback) = > {
return new Promise((resolve, reject) = > {
try {
setTimeout(() = > {
const res = callback && callback();
resolve(res);
}, 1000);
} catch(e) { reject(e); }}); };// Do not receive if you do not use the done method, otherwise an error will be reported
// We need to return the promise
test("The promise is fulfilled.".() = > {
expect.assertions(1);
const callback = () = > 1;
return delayPromise(callback).then((res) = > {
expect(res).toBe(1);
});
});
// Jest's built-in convergent approach
test("Promise uses jest's built-in convergent approach.".() = > {
expect.assertions(1);
const callback = () = > 1;
return expect(delayPromise(callback)).resolves.toBe(1);
});
// Async does not need return
test("Promise async is executed".async () => {
expect.assertions(1);
const callback = () = > 1;
const res = await delayPromise(callback);
expect(res).toBe(1);
});
Copy the code
mock
Create a mock function
jest.fn()
- Used to create a mock function
- Parameter: optional (function)
- A mock instance is returned without passing, and you can do something with some methods on the instance
- Pass arguments, mock this function, or call instance methods
The mock property of the mock function
- All mock functions have one
.mock
Property (object type), through this property can see how the function is called, etc., to achieve monitoring effectcalls
Get the information about the function
length
Get the number of times the function is executedresults
Retrieves the result of the specified index (starting with 0)
/ / the mock test
function forEach(items, callback) {
for (let index = 0; index < items.length; index++) { callback(items[index]); }}const mockCallback = jest.fn((x) = > 42 + x);
forEach([0.1], mockCallback);
it("The mock test".() = > {
// Get the number of times the function is executed
expect(mockCallback.mock.calls.length).toBe(2);
//console.log(mockCallback.mock.calls);
//calls[0][0], the first [0] represents the first execution, and the second [0] represents the first argument
// Get the first argument when the function is executed for the first time
expect(mockCallback.mock.calls[0] [0]).toBe(0);
// get the first argument for the second execution
expect(mockCallback.mock.calls[1] [0]).toBe(1);
// Get the result of the first execution
expect(mockCallback.mock.results[0].value).toBe(42);
//[undefined, undefined]
console.log(mockCallback.mock.instances);
});
Copy the code
instances
Get this to the collection
it("Test the mock's this to point to the collection".() = > {
const myMock = jest.fn();
const a = new myMock();
const b = { b: 1 };
const bound = myMock.bind(b);
// There will be a second {b: 1} only if the function is executed, because bind just points this to staging, but it hasn't been executed yet
bound();
console.log(myMock.mock.instances);
// > [ mockConstructor {}, { b: 1 } ]
});
Copy the code
mockReturnValue()
The value returned by the simulated function
- Just put the specified value inside the parentheses of the mockReturnValue
- MockReturnValue affects the next test and needs to be used
mockRestore
Clearance side effect
// Two are equal
const callback = () = > 1;
const callback = jest.fn().mockReturnValue(1);
Copy the code
spyOn
- Listen for a method or value
- Two parameters
- class
- The method name
- Hijacking can be done with mockReturnValue
mockRestore
- Clearance side effect
// Returns a random number between 0 and 10
const getRandom = () = > {
return Math.floor(Math.random() * 10);
};
test("Random number test, less than 10".() = > {
expect(getRandom()).toBeLessThan(10);
});
test("Random number test, greater than or equal to 0".() = > {
expect(getRandom()).toBeGreaterThanOrEqual(0);
});
test("Math.random returns 1, resulting in 10".() = > {
// spyOn listens, then controls the return value using mockReturnValue
const mockRandom = jest.spyOn(Math."random");
mockRandom.mockReturnValue(1);
expect(getRandom()).toBe(10);
// Use mockRestore to remove side effects. If you do not remove side effects, the next step is 10, which will definitely fail
mockRandom.mockRestore();
});
test("Does the last step affect me? Less than 10.".() = > {
expect(getRandom()).toBeLessThan(10);
});
Copy the code
mockReturnValueOnce()
Same as above, the difference is that it is only simulated once (can be called continuously)
const myMock = jest.fn();
it("Simulated return value".() = > {
myMock.mockReturnValueOnce(10);
console.log(myMock(), myMock());
// > 10, undefined, true, true
});
it("Let's see if it affects the next one.".() = > {
console.log(myMock());
//undefined is not affected
});
it("Can be called continuously.".() = > {
myMock.mockReturnValueOnce(10).mockReturnValueOnce("x").mockReturnValue(true);
console.log(myMock(), myMock(), myMock(), myMock());
// 10, 'x', true, true
});
Copy the code
mockImplementationOnce
Same as above, just to simulate the function, also oncemockName
- Function: Analog name, officially given function is easier to locate error messages
getMockName
- Use this method to get the alias name
const myMockFn = jest
.fn()
.mockReturnValue("default")
.mockImplementation((scalar) = > 42 + scalar)
.mockName("add42");
myMockFn.getMockName(); //"add42s"
Copy the code
- Syntactic sugar
it(Grammar candy.() = > {
myMockFn(1);
myMockFn();
//myMockFn is called at least 2 times, rule 1+1,toBeGreaterThan can be negative
expect(myMockFn.mock.calls.length).toBeGreaterThan(1);
// The function executes the type at least once with this argument
expect(myMockFn.mock.calls).toContainEqual([1]);
// The last time the function was called, the parameter was specified, we did not pass the parameter, so it is [].
expect(myMockFn.mock.calls[myMockFn.mock.calls.length - 1]).toEqual([]);
// When the function is called for the first time, the first argument is 1
expect(myMockFn.mock.calls[0] [0]).toBe(1);
// Whether the function alias is "add42"
expect(myMockFn.getMockName()).toBe("add42");
});
Copy the code
Simulation module
- You need to use
jest.mock
methods
// users.js
import axios from "axios";
class Users {
static all() {
return axios.get("/users.json").then((resp) = >resp.data); }}export default Users;
//__tests__/mockTest.js
import axios from "axios";
import Users from ".. /src/users";
jest.mock("axios");
test("should fetch users".() = > {
const users = [{ name: "Bob" }];
const resp = { data: users };
axios.get.mockResolvedValue(resp);
//data is [{name: "Bob"}]
return Users.all().then((data) = > expect(data).toEqual(users));
});
Copy the code
Jest. Mock can also pass callback
- When importing a module, you can pass in a callback if you want to do something to the entire module
jest.mock
The callback will be up thereThe console. The log before the execution
, and callbackThe return value overrides the value received by import
, undefined if not returned- Used when used in a callback
jest.requireActual
Method to import
//mockObj.js
export const mockObj = {
a: 1.b: () = > {
return "b"; }};export default() = >"default";
//__tests__/test.js
import defaultFn from ".. /src/mockObj";
console.log(defaultFn, 2); //{a:9090}
jest.mock(".. /src/mockObj".() = > {
const old = jest.requireActual(".. /src/mockObj");
console.log(old, "1");
/* { default: () => "default", mockObj: { a: 1, b: () => { return "b"; }}} * /
return {
a: 9090}; });Copy the code
Batch processing
beforeEach
/beforeAll
- BeforeEach executes before a test, once for each test instance execution
- BeforeAll is executed before the test, but only once, on the normal scope
- Parameters, callbak
afterEach
/afterAll
- Execute after test, same as above
- Parameters, callbak
- Usage scenarios
- One thing is used in multiple tests
/ / before processing
test("Math.random returns 1, resulting in 10".() = > {
const mockRandom = jest.spyOn(Math."random");
mockRandom.mockReturnValue(1);
expect(getRandom()).toBe(10);
mockRandom.mockRestore();
});
test("Math.random returns 0.1, resulting in 1".() = > {
const mockRandom = jest.spyOn(Math."random");
mockRandom.mockReturnValue(0.1);
expect(getRandom()).toBe(1);
mockRandom.mockRestore();
});
//可以看到都调用了jest.spyOn(Math, 'random');和mockRandom.mockRestore();
// It can be handled at this point
beforeEach(() = > {
mockRandom = jest.spyOn(Math."random");
});
afterEach(() = > {
mockRandom.mockRestore();
});
test("Math.random returns 1, resulting in 10".() = > {
mockRandom.mockReturnValue(1);
expect(getRandom()).toBe(10);
});
test("Math.random returns 0.1, resulting in 1".() = > {
mockRandom.mockReturnValue(0.1);
expect(getRandom()).toBe(1);
});
Copy the code
Snapshot (testing UI components)
- Install dependencies
npm install @babel/core @babel/preset-env @babel/preset-react react-test-renderer -D
npm install react -S
- create
.babelrc
//./babelrc
{
"presets": ["@babel/preset-env"."@babel/preset-react"]}Copy the code
- Creating UI Components
//./BaiduLink.js
import React from "react";
const BaiduLink = () = > {
const url = "https://www.baidu.com";
const text = "Google it.";
return <a href={url}>{text}</a>;
};
module.exports = BaiduLink;
Copy the code
- Create test files
- We used it here
react-test-renderer
This library helps us generate mapping files- use
create
A snapshot is generated.
Outreach snapshot
- use
toMatchSnapshot
Compare old and new mappings- Outreach will be generated
__snapshots__
Folder, which contains the generated mapping file (suffix.snap), written below
//./__tests__/BaiduLink.js
import Link from ".. /BaiduLink";
import renderer from "react-test-renderer";
import React from "react";
it("I'm an outreach snapshot.".() = > {
const tree = renderer.create(
<Link page="https://www.baidu.com/">The baidu</Link>
);
expect(tree).toMatchSnapshot();
});
Copy the code
- Start testing,
npm test
- The first test will be in
__teste__
Directory generation__snapshots__
Folder containing the generated mapping files (suffix.snap)- Suppose we generate the mapping file and modify it again
./BaiduLink.js
, execute againnpm test
Will throw an exception
- Because it will compare this mapping to the previous mapping (so, if you create it, make sure to commit the generated mapping file when git add)
- If you want to override the original mapping, add it in the package.json command
jest --updateSnapshot
Then execute (a new mapping is generated and the previous one is overwritten)
Inline snapshot
toMatchInlineSnapshot
- When first used, this method does not pass values
- When we execute
npm test
Later,The mapping structure is generated inside the parentheses of the method
- Instead of generating files externally, it passes them to itself, which is the difference between it and outreach
it("I'm an inline snapshot.".() = > {
const tree = renderer.create(
<Link page="https://www.baidu.com/">The baidu</Link>
);
// The first time the method does not pass an argument
expect(tree).toMatchInlineSnapshot();
});
// After the first execution, it places the generated mapping file in function parentheses
Const tree = renderer.create(
); Expect (tree).tomatchinlinesnapshot (' '); }); * /
Copy the code
Create snapshots of unstable data
- So let’s say I store a piece of data
new Date
It will be different every time, so it will report an error every time- This can be done within the toMatchSnapshot function, assert any, and it will save the snapshot for each update without error
it("Test Object Snapshot".() = > {
const user = {
createdAt: new Date(),
id: Math.floor(Math.random() * 20),
name: "LeBron James"};// If no parameter is passed in the current example, every save after the first one will fail
expect(user).toMatchSnapshot({
// Declare it as any
createdAt: expect.any(Date),
id: expect.any(Number)}); });Copy the code
Enable test coverage
- To the end of the package.json test command
--coverage
- After execution, the coverage table is displayed, and
Automatically generate the Coverage folder
It contains detailed information
- We can open it
coverage/lcov-report/index.html
File to view details, clickfile
For the corresponding file, you can see the specific code
- If it’s a red background it’s not covered
- The number in front of the line represents how many times this line of code was executed in this test
The react test
@testing-library/react
- The installation
npm install @testing-library/react @testing-library/jest-dom -D
- Needed before use
Set jEST's environment to JsDOM
- Create the jest. Config. js file in the root directory
https://jestjs.io/docs/configuration
- content
module.exports = {
setupFilesAfterEnv: ["@testing-library/jest-dom"].testEnvironment: "jsdom"};Copy the code
- test
import React from "react";
import { render } from "@testing-library/react";
test("component".() = > {
const { getByLabelText } = render(<button aria-label="Button" />);
expect(getByLabelText("Button")).toBeEmptyDOMElement();
});
Copy the code
React-dom comes with a test libraryreact-dom/test-utils
- You can deconstruct it to get specific methods
act
- One argument, callback
- Render UI components, not only for the first rendering, but also for state update operations
- Used before assertion to ensure that our component has been rendered
it("The use of act".() = > {
act(() = >{reactdom.render (< component />, DOM node); }); });Copy the code
Simulate
- Used to test events, he is better than the native
MouseEvent
A lot easier to use- Usage:
Simulate. Event (node node,{parameters})
The second parameter is optional
// Mimic the change event
import React, { useState } from "react";
const Input = () = > {
const [val, setVal] = useState("12");
return (
<input
type="text"
className="input"
value={val}
onChange={(e)= >{ setVal(e.target.value); // setVal('100'); }} / >
);
};
export default Input;
// Test the file
import React from "react";
import InputChange from ".. /src/component/InputChange";
import { act, Simulate } from "react-dom/test-utils";
import { render, unmountComponentAtNode } from "react-dom";
let container = null;
beforeEach(() = > {
// Create a DOM element as the render target
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() = > {
// Clean up on exit
unmountComponentAtNode(container);
container.remove();
container = null;
});
it("Change event".() = > {
act(() = > {
render(<InputChange />, container);
});
let input = container.querySelector(".input");
expect(input.value).toBe("12"); / / success
act(() = > {
Simulate.change(input, { target: { value: "123"}}); });console.log(input.value); / / 123
});
Copy the code
MouseEvent
- Native simulation events, not recommended
- Function: Tests DOM events
- Method of use
Dom.dispatchevent (new MouseEvent(" event ", {bubbles: true}));
Pay attention to
{bubbles:true} is required for events to be dispatched so that react can listen to them- During the test
innerText
Could not get value, need to usetextContent
//BtnClick.tsx
import React, { useState } from "react";
const BtnClick = () = > {
const [num, setNum] = useState(0);
return (
<>
<div className="num">Current number {num}</div>
<div
className="Btn"
onClick={()= >{ setNum((old) => { return old + 1; }); }} > Click +1</div>
</>
);
};
// interface BtnClick {
// state: any
// setState: any
// }
// class BtnClick extends React.Component {
// constructor(props) {
// super(props);
// this.state = { num: 0 };
// this.handleClick = this.handleClick.bind(this);
/ /}
// handleClick() {
// this.setState(state => ({
// num: state.num + 1,
/ /}));
/ /}
// render() {
// return (
// <>
//
current number {this.state.num}
click +1
//
/ /)
/ /}
// }
export default BtnClick;
//test.jsx
import React from "react";
import BtnClick from ".. /src/component/BtnClick";
import { render, unmountComponentAtNode } from "react-dom";
import { act, Simulate } from "react-dom/test-utils";
let container = null;
beforeEach(() = > {
// Create a DOM element as the render target
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() = > {
// Clean up on exit
unmountComponentAtNode(container);
container.remove();
container = null;
});
it("Click event".() = > {
act(() = > {
render(<BtnClick />, container);
});
let num = container.querySelector(".num");
console.log(num.textContent);
// expect(numValue).tobe (" 0");
act(() = > {
container.querySelector(".Btn").dispatchEvent(
new MouseEvent("click", {
bubbles: true,}));// This is ok
//Simulate.click(container.querySelector(".Btn"));
});
console.log(num.textContent);
});
Copy the code