跳到主要内容

jest

基础

安装
pnpm add --save-dev jest
pnpm add --save-dev ts-jest
pnpm add --save-dev @types/jest
基本命令
# 测试
npx jest src/
# 覆盖率测试
npx jest --coverage src/

匹配器

相等

// 直接比较原始类型或引用
test("two plus two is four", () => {
expect(2 + 2).toBe(4);
});

// 递归比较属性相等
// 忽略 undefined 属性
test("object assignment", () => {
const data = { one: 1 };
data["two"] = 2;
expect(data).toEqual({ one: 1, two: 2 });
});

布尔判断

class A {}
test("null", () => {
const n = null;
expect(n).toBeNull();
expect(n).toBeDefined();
expect(n).not.toBeUndefined();
expect(n).not.toBeTruthy();
expect(n).toBeFalsy();
expect(new A()).toBeInstanceOf(A);
});

数字

test("two plus two", () => {
const value = 2 + 2;
expect(value).toBeGreaterThan(3);
expect(value).toBeGreaterThanOrEqual(3.5);
expect(value).toBeLessThan(5);
expect(value).toBeLessThanOrEqual(4.5);

// toBe and toEqual are equivalent for numbers
expect(value).toBe(4);
expect(value).toEqual(4);
});

正则表达式

test("there is no I in team", () => {
expect("team").not.toMatch(/I/);
});

数组

const shoppingList = [
"diapers",
"kleenex",
"trash bags",
"paper towels",
"milk",
];

test("the shopping list has milk on it", () => {
expect(shoppingList).toContain("milk");
expect(new Set(shoppingList)).toContain("milk");
});

异常

function compileAndroidCode() {
throw new Error("you are using the wrong JDK!");
}

// 使用箭头函数包裹
test("compiling android goes as expected", () => {
expect(() => compileAndroidCode()).toThrow();
expect(() => compileAndroidCode()).toThrow(Error);
});

异步

promise

test("the data is peanut butter", () => {
return fetchData().then((data) => {
expect(data).toBe("peanut butter");
});
});

async/await

test("the data is peanut butter", async () => {
const data = await fetchData();
expect(data).toBe("peanut butter");
});

test("the fetch fails with an error", async () => {
expect.assertions(1);
try {
await fetchData();
} catch (e) {
expect(e).toMatch("error");
}
});

.resolves / .rejects

test("the data is peanut butter", () => {
return expect(fetchData()).resolves.toBe("peanut butter");
});

test("the fetch fails with an error", () => {
return expect(fetchData()).rejects.toMatch("error");
});

预先设置

重复设置

// 每次执行前运行
beforeEach(() => {
initializeCityDatabase();
});

// 每次执行后运行
afterEach(() => {
clearCityDatabase();
});

一次性设置

// 所有测试执行前运行一次
beforeAll(() => {
return initializeCityDatabase();
});

// 所有测试执行后运行一次
afterAll(() => {
return clearCityDatabase();
});

作用域

// 应用于所有测试
beforeEach(() => {
return initializeCityDatabase();
});

describe("matching cities to foods", () => {
// 只应用于该 describe
beforeEach(() => {
return initializeFoodDatabase();
});
});

模拟函数

使用模拟函数

// forEach.js
export function forEach(items, callback) {
for (let index = 0; index < items.length; index++) {
callback(items[index]);
}
}

// forEach.test.js
const forEach = require("./forEach");
const mockCallback = jest.fn((x) => 42 + x);

test("forEach mock function", () => {
forEach([0, 1], mockCallback);

// The mock function was called twice
expect(mockCallback.mock.calls).toHaveLength(2);

// The first argument of the first call to the function was 0
expect(mockCallback.mock.calls[0][0]).toBe(0);

// The first argument of the second call to the function was 1
expect(mockCallback.mock.calls[1][0]).toBe(1);

// The return value of the first call to the function was 42
expect(mockCallback.mock.results[0].value).toBe(42);
});

.mock 属性

.mock 属性
  • 存储函数的参数和返回值;
常用属性
// The function was called exactly once
expect(someMockFunction.mock.calls).toHaveLength(1);

// The first arg of the first call to the function was 'first arg'
expect(someMockFunction.mock.calls[0][0]).toBe("first arg");

// The second arg of the first call to the function was 'second arg'
expect(someMockFunction.mock.calls[0][1]).toBe("second arg");

// The return value of the first call to the function was 'return value'
expect(someMockFunction.mock.results[0].value).toBe("return value");

// The function was called with a certain `this` context: the `element` object.
expect(someMockFunction.mock.contexts[0]).toBe(element);

// This function was instantiated exactly twice
expect(someMockFunction.mock.instances.length).toBe(2);

// The object returned by the first instantiation of this function
// had a `name` property whose value was set to 'test'
expect(someMockFunction.mock.instances[0].name).toBe("test");

// The first argument of the last call to the function was 'test'
expect(someMockFunction.mock.lastCall[0]).toBe("test");

mock 返回值

const myMock = jest.fn();
console.log(myMock());
// > undefined

myMock.mockReturnValueOnce(10).mockReturnValueOnce("x").mockReturnValue(true);

console.log(myMock(), myMock(), myMock(), myMock());
// > 10, 'x', true, true

配置文件

node
/** @type {import('ts-jest').JestConfigWithTsJest} */
const config = {
verbose: true,
preset: "ts-jest",
testEnvironment: "node",
transform: {
"^.+\\.ts?$": [
"ts-jest",
{
isolatedModules: true,
diagnostics: false,
},
],
},
};

export default config;
react
/** @type {import('ts-jest').JestConfigWithTsJest} */
const config = {
verbose: true,
preset: "ts-jest",
testEnvironment: "jsdom",
transform: {
"^.+\\.ts?$": [
"ts-jest",
{
isolatedModules: true,
diagnostics: false,
},
],
},
};

export default config;