基础语法
基础语法
我们从一个基本的 Math 模块开始。首先创建一个 math.js 文件:
// basic/math.js
const sum = (a, b) => a + b;
const mul = (a, b) => a * b;
const sub = (a, b) => a - b;
const div = (a, b) => a / b;
export { sum, mul, sub, div };
要测试这个 Math 模块是否正确,我们需要编写测试代码。通常,测试文件与所要测试的源码文件同名,但是后缀名为 .test.js 或者 .spec.js。我们这里则创建一个 math.test.js 文件:
// basic/math.test.js
import { sum, mul, sub, div } from "./math";
test("Adding 1 + 1 equals 2", () => {
expect(sum(1, 1)).toBe(2);
});
test("Multiplying 1 * 1 equals 1", () => {
expect(mul(1, 1)).toBe(1);
});
test("Subtracting 1 - 1 equals 0", () => {
expect(sub(1, 1)).toBe(0);
});
test("Dividing 1 / 1 equals 1", () => {
expect(div(1, 1)).toBe(1);
});
执行 npm test jest
将会执行所有匹配的测试文件,并最终返回测试结果。
匹配器
匹配器用来实现断言功能。在前面的例子中,我们只使用了 toBe() 匹配器:
test("Adding 1 + 1 equals 2", () => {
expect(sum(1, 1)).toBe(2);
});
在此代码中,expect(sum(1, 1)) 返回一个“期望”对象,.toBe(2) 是匹配器。匹配器将 expect() 的结果(实际值)与自己的参数(期望值)进行比较。当 Jest 运行时,它会跟踪所有失败的匹配器,并打印出错误信息。
常用的匹配器如下:
toBe
使用 Object.is 判断是否严格相等。toEqual
递归检查对象或数组的每个字段。toBeNull
只匹配null
。toBeUndefined
只匹配undefined
。toBeDefined
只匹配非undefined
。toBeTruthy
只匹配真。toBeFalsy
只匹配假。toBeGreaterThan
实际值大于期望。toBeGreaterThanOrEqual
实际值大于或等于期望值toBeLessThan
实际值小于期望值。toBeLessThanOrEqual
实际值小于或等于期望值。toBeCloseTo
比较浮点数的值,避免误差。toMatch
正则匹配。toContain
判断数组中是否包含指定项。.toHaveProperty(keyPath, value)
判断对象中是否包含指定属性。toThrow
判断是否抛出指定的异常。toBeInstanceOf
判断对象是否是某个类的实例,底层使用instanceof
。
所有的匹配器都可以使用 .not
取反:
test("Adding 1 + 1 does not equal 3", () => {
expect(1 + 1).not.toBe(3);
});
复制代码;
对于 Promise 对象,我们可以使用 .resolves
和 .rejects
:
// .resolves
test("resolves to lemon", () => {
// make sure to add a return statement
return expect(Promise.resolve("lemon")).resolves.toBe("lemon");
});
// .rejects
test("rejects to octopus", () => {
// make sure to add a return statement
return expect(Promise.reject(new Error("octopus"))).rejects.toThrow(
"octopus"
);
});
钩子函数
Jest 为我们提供了四个测试用例的钩子:beforeAll()、afterAll()、beforeEach()、afterEach()。beforeAll() 和 afterAll() 会在所有测试用例之前和所有测试用例之后执行一次。beforeEach() 和 afterEach() 会在每个测试用例之前和之后执行。
describe
我们可以使用 describe
将测试用例分组,在 describe
块中的钩子函数只作用于块内的测试用例:
beforeAll(() => console.log("1 - beforeAll")); // 1
afterAll(() => console.log("1 - afterAll")); // 12
beforeEach(() => console.log("1 - beforeEach")); // 2,6
afterEach(() => console.log("1 - afterEach")); // 4,10
test("", () => console.log("1 - test")); // 3
describe("Scoped / Nested block", () => {
beforeAll(() => console.log("2 - beforeAll")); // 5
afterAll(() => console.log("2 - afterAll")); // 11
beforeEach(() => console.log("2 - beforeEach")); // 7
afterEach(() => console.log("2 - afterEach")); // 9
test("", () => console.log("2 - test")); // 8
});
需要注意的是,顶级的 beforeEach
会在 describe
块内的 beforeEach
之前执行。Jest 会先执行 describe
块内的操作,等 describe
块内的操作执行完毕后,按照出现在 describe
中的先后顺序执行测试用例,因此初始化和销毁操作应该放在钩子函数中运行,而不是 describe
块内:
describe("outer", () => {
console.log("describe outer-a"); // 1
describe("describe inner 1", () => {
console.log("describe inner 1"); // 2
test("test 1", () => {
console.log("test for describe inner 1"); // 6
expect(true).toEqual(true);
});
});
console.log("describe outer-b"); // 3
test("test 1", () => {
console.log("test for describe outer"); // 7
expect(true).toEqual(true);
});
describe("describe inner 2", () => {
console.log("describe inner 2"); // 4
test("test for describe inner 2", () => {
console.log("test for describe inner 2"); // 8
expect(false).toEqual(false);
});
});
console.log("describe outer-c"); // 5
});
jest-when
jest-when 允许你使用一组原始的 Jest mock 函数,以便只根据你的 mock 函数被调用的参数来训练你的 mock。
import { when } from "jest-when";
const fn = jest.fn();
when(fn).calledWith(1).mockReturnValue("yay!");
expect(fn(1)).toEqual("yay!");
when(fn)
.calledWith(1)
.mockReturnValue("yay!")
.calledWith(2)
.mockReturnValue("nay!");
expect(fn(1)).toEqual("yay!");
expect(fn(2)).toEqual("nay!");