小白逆袭----2025了,彻底弄懂react-test单元测试 基础使用(一)
1.jest的基础使用
1.1安装jest
npm install --save-dev jest
1.2 快速入门
const sum = function(a, b){return a + b + 1;
}const sub = function(a, b){return a - b;
}
describe("这是一组测试,测试加减法", () => {// 回调函数中就放一个一个的测试用例/*** 一个 test 方法意味着书写了一个测试用例* param1 :针对这个测试用例的一个描述* param2 :执行该用例所对应的回调函数*/test("测试加法", () => {expect(sum(1, 2)).toBe(3);});test("测试减法", () => {expect(sub(10, 5)).toBe(5);});
});
2.匹配器
2.1 常用的匹配器
(1)toBe 和 toEqual
toEqual可以针对对象深比较
示例:
test("深度比较对象",()=>{const stu = {name : "张三", score : {html : 100, css : 90}};expect(stu).not.toBe({name : "张三", score : {html : 100, css : 90}});// 使用 toEqual 来进行深度比较// toEqual 会递归比较对象的所有属性expect(stu).toEqual({name : "张三", score : {html : 100, css : 90}});
})
(2) 布尔匹配器
- toBeFalsy
- toBeTruthy
- toBeNull
- toBeDefined
- toBeUndefined
示例:
test("无参匹配器",()=>{const n = null;expect(n).toBeNull();expect(n).toBeDefined();expect(n).not.toBeUndefined();const a = 0;expect(a).not.toBeNull();expect(a).toBeDefined();expect(a).not.toBeUndefined();
})
(3) 数值相关匹配器
- toBeGreaterThan 大于
- toBeGreaterThanOrEqual 大于等于
- toBeLessThan 小于
- toBeLessThanOrEqual 小于等于
浮点数
- toBeCloseTo
接受二个参数,第二个参数用于指定位数,默认是两位
示例:
test("数值相关匹配器", () => {const value1 = 4;// 大于expect(value1).toBeGreaterThan(3);// 大于等于expect(value1).toBeGreaterThanOrEqual(4);// 小于expect(value1).toBeLessThan(5);// 小于等于expect(value1).toBeLessThanOrEqual(4);// 这里需要注意一下浮点数const value2 = 0.1 + 0.2;expect(value2).toBeCloseTo(0.3);// toBeCloseTo 还接受第二个参数,第二个参数用于指定位数,默认是两位expect(0.302).toBeCloseTo(0.301);expect(0.302).not.toBeCloseTo(0.301, 5);
});
(4) 字符串匹配
- toMatch 正则匹配
示例:
test("字符串相关匹配器",()=>{expect("this is a test").toMatch(/test/);expect("this is a test").not.toMatch(/abc/);
})
(5)数组匹配器
- toContain
检测一个数组是否包含该子项
也可以用来检测一个字符串是否是另一个字符串的子串
示例:
const shoppingList = ["diapers","kleenex","trash bags","paper towels","milk",
];
test("数组相关匹配器", () => {expect(shoppingList).toContain("milk");// toContain 进行的是全等比较,也就是严格比较expect([1, 2, 3]).not.toContain("1");expect([{ name: "张三" }, { name: "李四" }]).not.toContain({ name: "张三" });// toContain 还可以用来检测一个字符串是否是另一个字符串的子串expect("this is a test").toContain("test");// 也可以用到集合(set)里面expect(new Set(shoppingList)).toContain("milk");
});
(6) 异常匹配器
- toThrow 测试某个函数,调用之后是否会抛出异常
function compileCode(){throw new Error("aaa you are using the wrong JDK bbb");
}test("异常相关的匹配器",()=>{expect(()=>compileCode()).toThrow();// toThrow 里面可以传递不同的参数expect(()=>compileCode()).toThrow(Error);expect(()=>compileCode()).toThrow("you are using the wrong JDK");expect(()=>compileCode()).toThrow(/JDK/);
})
(7) 非对称匹配器
- expect.arrayContaining([a, b]) 断言目标数组 至少包含 a 和 b
- expect.not.arrayContaining([a, b]) 断言目标数组 不包含 a 或 b
示例
const arr = ["张三"];
test("上面的数组不包含某一项", () => {expect(["李四", "王武", "赵六"]).toEqual(expect.not.arrayContaining(arr));
});const obj = {name : "张三"};
test("对象不包含上面的键值对",()=>{expect({age : 18}).toEqual(expect.not.objectContaining(obj));expect({name: "李四",age : 18}).toEqual(expect.not.objectContaining(obj));
})
3. 生命周期
- Jest 中的生命周期方法分为重复性的和一次性的
- 重复性的生命周期方法是指每个测试用例前后都会执行
- beforeEach
- afterEach
- 一次性的生命周期方法是指只会执行一次方法
- beforeAll
- afterAll
` `describe('工具函数', () => {beforeAll(() => {console.log("beforeAll")})beforeEach(() => {console.log("beforeEach")})afterAll(() => {console.log("afterAll")})afterEach(() => {console.log("afterEach")})it('add', () => {// 测试加法console.log('测试加法')expect(add(1, 2)).toBe(3);});
});//输出结果
//beforeAll => beforeEach => 测试加法 => afterEach => afterAll
- 一个分组中也能书写局部生命周期方法,但需注意和全局方法之间的顺序关系
- 使用 test.only 可以很方便地运行单个测试用例,以便在调试失败的测试用例时进行测试
示例:
test.only("测试乘法函数", () => {const result = mul(2, 3);expect(result).toBe(6);console.log("\x1b[31m%s\x1b[0m", "测试乘法函数");
});
4.模拟函数
jest.fn(implementation?)
implementation 是一个可选参数,代表着模拟函数的实现,如果没有传入,那么创建的是一个空的模拟函数。
mock.mockReturnValue(30) // 设置返回值为 30
mockReturnValueOnce(10) // 第一次调用模拟函数对应的返回值
mockReturnValueOnce(20) // 第二次调用模拟函数对应的返回值
示例:
test("基本演示",()=>{// 创建一个模拟函数const mock = jest.fn();// 设置这个模拟函数的返回值为 42mock.mockReturnValue(42);expect(mock()).toBe(42);
});//也可以传入一个函数来代表模拟函数的实现,
test("内置实现",()=>{const mock = jest.fn(x => 100 + x);expect(mock(1)).toBe(101);
})
5.模拟组件
快速入门示例
/*** 和请求相关的*/const axios = require("axios");class User {/*** 获取所有的用户*/static all() {return axios.get("/users.json").then((resp) => resp.data);}
}module.exports = User;
//我们使用 jest.mock 来模拟 axios 这个模块,如下:
const User = require("../api/userApi");
const userData = require("./user.json");// 模拟 axios 模块
jest.mock("axios", () => {const userData = require("./user.json");// 模拟响应数据const resp = {data: userData,};return {get: jest.fn(() => Promise.resolve(resp)),};
});// 测试用例
test("测试获取用户数据", async () => {// 现在我们已经模拟了 axios// 但是目前的 axios 没有书写任何的行为// 因此我们需要在这里进行一个 axios 模块行为的指定// 指定了在使用 axios.get 的时候返回 resp 响应// axios.get.mockImplementation(()=>Promise.resolve(resp));await expect(User.all()).resolves.toEqual(userData);
});
6.jest配置文件
生成配置文件指令:
npx jest --init
生成配置文件如下图所示:
常用的配置项:
1. collectCoverage:会收集并显示测试覆盖率,包含每个文件中每种类型的代码(语句、分支、函数和行)的测试覆盖率

在上面的表格中,我们能够看到如下的信息:
- % Stmts:包含语句的百分比,即被测试覆盖的语句占总语句数的比例。
- % Branch:包含分支的百分比,即被测试覆盖的分支占总分支数的比例。
- % Funcs:包含函数的百分比,即被测试覆盖的函数占总函数数的比例。
- % Lines:包含行的百分比,即被测试覆盖的行占总行数的比例。
- Uncovered Line #s:未被测试覆盖的行号。
另外:
当 collectCoverage 设置为 true 之后,还可以设置 coverageThreshold 代码覆盖率的阀值:
module.exports = {// ...collectCoverage: true,coverageThreshold: {global: {branches: 90,functions: 90,lines: 90,statements: 90,},},// ...
};
另外,在项目根目录下面,还新生成了一个 coverage 的目录,里面其实就是各种格式(xml、json、html)的测试报告,之所以生成不同格式的报告,是为了方便你后面通过不同的工具来进行读取。
例如 HTML 版本的测试报告如下图所示:
2. testMatch:这个配置项可以指定 Jest 应该运行哪些测试文件。
默认情况下, Jest 会查找 .test.js 或者
.spec.js 结尾的文件
例如我们将该配置修改为如下:
testMatch: ["**/test/**/*.[jt]s?(x)",
],
3. moduleFileExtensions :指定 Jest 查找测试文件时应该搜索哪些文件扩展名。
// moduleFileExtensions: [// "js",// "mjs",// "cjs",// "jsx",// "ts",// "mts",// "cts",// "tsx",// "json",// "node"// ],
- setupFilesAfterEnv:指定 Jest 在运行测试之前应该运行哪些文件。
例如:
setupFilesAfterEnv: ['<rootDir>/src/setupTests.js']
在执行每个测试套件(文件)之前,都会先执行这个 setupTests 文件