Jest 中的生命周期方法
在 Jest 测试框架中,生命周期方法用于在测试用例执行前后执行特定的操作。这些方法可以帮助你在测试前设置环境、清理资源等。Jest 提供了两类生命周期方法:
- 重复性的生命周期方法
beforeEach
afterEach
- 一次性的生命周期方法
beforeAll
afterAll
这些方法都是全局方法,无需引入,直接在测试文件中使用即可。
重复性的生命周期方法
beforeEach
和 afterEach
beforeEach
和 afterEach
方法会在每个测试用例的前后执行。这对于需要在每个测试用例之前设置初始状态或在每个测试用例之后清理资源非常有用。
示例代码
const { sum, sub, mul, div } = require("./tools");
// beforeEach 会在执行每一个测试用例之前被触发
beforeEach(() => {
console.log("全局的beforeEach");
});
afterEach(() => {
console.log("全局的afterEach");
});
test("测试加法函数", () => {
const result = sum(1, 3);
expect(result).toBe(4);
console.log("\x1b[31m%s\x1b[0m", "测试加法函数");
});
test("测试减法函数", () => {
const result = sub(15, 10);
expect(result).toBe(5);
console.log("\x1b[31m%s\x1b[0m", "测试减法函数");
});
test("测试乘法函数", () => {
const result = mul(2, 3);
expect(result).toBe(6);
console.log("\x1b[31m%s\x1b[0m", "测试乘法函数");
});
test("测试除法函数", () => {
const result = div(50, 2);
expect(result).toBe(25);
console.log("\x1b[31m%s\x1b[0m", "测试除法函数");
});
执行顺序
- 每个测试用例执行前,先执行全局的
beforeEach
。 - 测试用例执行完毕后,执行全局的
afterEach
。
一次性的生命周期方法
beforeAll
和 afterAll
beforeAll
和 afterAll
方法分别在所有测试用例执行之前和之后执行一次。这对于需要在所有测试用例之前进行一次性设置或在所有测试用例之后进行一次性清理非常有用。
示例代码
// ...
// beforeAll 是在整个测试套件的第一个测试用例执行之前执行
beforeAll(() => {
console.log("全局的beforeAll");
});
// afterAll 会在所有测试用例执行完成之后,然后再执行 afterAll
afterAll(() => {
console.log("全局的afterAll");
});
// ...
执行顺序
- 所有测试用例执行之前,先执行全局的
beforeAll
。 - 所有测试用例执行完毕后,执行全局的
afterAll
。
在分组中添加生命周期方法
如果测试用例较多,可以使用 describe
进行分组。在分组中也可以添加生命周期方法,这些方法仅对该组测试用例有效。
示例代码
// 第二组
describe("第二组", () => {
beforeEach(() => {
console.log("\x1b[34m%s\x1b[0m", "分组beforeEach");
});
afterEach(() => {
console.log("\x1b[34m%s\x1b[0m", "分组afterEach");
});
beforeAll(() => {
console.log("\x1b[32m%s\x1b[0m", "分组beforeAll");
});
afterAll(() => {
console.log("\x1b[32m%s\x1b[0m", "分组afterAll");
});
test("测试乘法函数", () => {
const result = mul(2, 3);
expect(result).toBe(6);
console.log("\x1b[31m%s\x1b[0m", "测试乘法函数");
});
test("测试除法函数", () => {
const result = div(50, 2);
expect(result).toBe(25);
console.log("\x1b[31m%s\x1b[0m", "测试除法函数");
});
});
执行顺序
- 全局的
beforeAll
先执行。 - 分组的
beforeAll
再执行。 - 每个测试用例执行前,先执行全局的
beforeEach
,再执行分组的beforeEach
。 - 每个测试用例执行完毕后,先执行分组的
afterEach
,再执行全局的afterEach
。 - 所有测试用例执行完毕后,先执行分组的
afterAll
,再执行全局的afterAll
。
补充:test.only
test.only
用于只运行特定的测试用例。这对于调试失败的测试用例非常有用。
示例代码
test.only("测试乘法函数", () => {
const result = mul(2, 3);
expect(result).toBe(6);
console.log("\x1b[31m%s\x1b[0m", "测试乘法函数");
});
执行顺序
- 使用
test.only
时,只会执行标记的测试用例。 - 对应的生命周期方法也会被执行。
源码解析
在 Jest 的源码中,这些生命周期方法的背后,实际上都是调用的同一个名为 _addHook
的方法。
const beforeEach: THook = (fn, timeout) =>
_addHook(fn, 'beforeEach', beforeEach, timeout);
const beforeAll: THook = (fn, timeout) =>
_addHook(fn, 'beforeAll', beforeAll, timeout);
const afterEach: THook = (fn, timeout) =>
_addHook(fn, 'afterEach', afterEach, timeout);
const afterAll: THook = (fn, timeout) =>
_addHook(fn, 'afterAll', afterAll, timeout);
const _addHook = (
fn: Circus.HookFn,
hookType: Circus.HookType,
hookFn: THook,
timeout?: number,
) => {
const asyncError = new ErrorWithStack(undefined, hookFn);
if (typeof fn !== 'function') {
asyncError.message =
'Invalid first argument. It must be a callback function.';
throw asyncError;
}
dispatchSync({ asyncError, fn, hookType, name: 'add_hook', timeout });
};
_addHook
方法主要负责错误处理,并调用 dispatchSync
方法来调度钩子函数的执行。
总结
- Jest 中的生命周期方法分为重复性和一次性两种。
- 重复性的生命周期方法:
beforeEach
:每个测试用例前执行。afterEach
:每个测试用例后执行。
- 一次性的生命周期方法:
beforeAll
:所有测试用例前执行一次。afterAll
:所有测试用例后执行一次。
- 在分组中可以添加局部的生命周期方法,但需注意与全局方法的执行顺序。
- 使用
test.only
可以方便地运行单个测试用例,以便在调试失败的测试用例时进行测试。