9.3.1 测试框架怎么选——Jest 配置:测试框架与断言库
Jest 是 JavaScript 生态中最流行的测试框架,零配置即可使用,功能强大且生态完善。
测试框架对比
| 特性 | Jest | Vitest | Mocha |
|---|---|---|---|
| 配置复杂度 | 低 | 低 | 高 |
| 速度 | 中 | 快 | 中 |
| TypeScript | 需 ts-jest | 原生支持 | 需配置 |
| 快照测试 | 内置 | 内置 | 需插件 |
| Mock | 内置 | 内置 | 需插件 |
| 生态 | 最成熟 | 快速成长 | 成熟 |
推荐:新项目使用 Vitest(更快),已有 Jest 项目继续使用 Jest(迁移成本)。
Jest 完整配置
安装依赖
bash
npm install -D jest ts-jest @types/jest配置文件
typescript
// jest.config.ts
import type { Config } from 'jest';
const config: Config = {
// 预设:支持 TypeScript
preset: 'ts-jest',
// 测试环境:Node.js(服务端)或 jsdom(浏览器)
testEnvironment: 'node',
// 测试文件位置
roots: ['<rootDir>/src', '<rootDir>/__tests__'],
// 测试文件匹配模式
testMatch: [
'**/__tests__/**/*.test.ts',
'**/__tests__/**/*.spec.ts',
],
// 忽略的目录
testPathIgnorePatterns: ['/node_modules/', '/dist/'],
// 路径别名(与 tsconfig.json 保持一致)
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
// 测试前执行的文件
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
// 覆盖率配置
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/index.ts',
],
coverageThreshold: {
global: {
branches: 70,
functions: 70,
lines: 70,
statements: 70,
},
},
// 超时时间(毫秒)
testTimeout: 10000,
// 清理 Mock
clearMocks: true,
restoreMocks: true,
};
export default config;初始化文件
typescript
// jest.setup.ts
import { PrismaClient } from '@prisma/client';
// 全局 Prisma 实例
const prisma = new PrismaClient();
// 测试前连接数据库
beforeAll(async () => {
await prisma.$connect();
});
// 测试后断开连接
afterAll(async () => {
await prisma.$disconnect();
});
// 扩展 expect 匹配器(可选)
expect.extend({
toBeWithinRange(received: number, floor: number, ceiling: number) {
const pass = received >= floor && received <= ceiling;
return {
message: () =>
`expected ${received} ${pass ? 'not ' : ''}to be within range ${floor} - ${ceiling}`,
pass,
};
},
});断言库使用
基础断言
typescript
describe('基础断言', () => {
// 相等性
it('toBe - 严格相等', () => {
expect(1 + 1).toBe(2);
expect('hello').toBe('hello');
});
it('toEqual - 深度相等', () => {
expect({ a: 1 }).toEqual({ a: 1 });
expect([1, 2, 3]).toEqual([1, 2, 3]);
});
// 真值判断
it('toBeTruthy / toBeFalsy', () => {
expect(1).toBeTruthy();
expect(0).toBeFalsy();
expect(null).toBeFalsy();
});
// 数值比较
it('数值断言', () => {
expect(10).toBeGreaterThan(5);
expect(10).toBeLessThanOrEqual(10);
expect(0.1 + 0.2).toBeCloseTo(0.3);
});
// 字符串匹配
it('字符串断言', () => {
expect('hello world').toContain('world');
expect('hello world').toMatch(/world$/);
});
// 数组包含
it('数组断言', () => {
expect([1, 2, 3]).toContain(2);
expect([{ id: 1 }, { id: 2 }]).toContainEqual({ id: 1 });
});
});异步断言
typescript
describe('异步断言', () => {
it('使用 async/await', async () => {
const result = await fetchData();
expect(result).toBe('data');
});
it('使用 resolves', async () => {
await expect(fetchData()).resolves.toBe('data');
});
it('使用 rejects', async () => {
await expect(fetchBadData()).rejects.toThrow('Error');
});
});对象匹配
typescript
describe('对象匹配', () => {
it('toMatchObject - 部分匹配', () => {
const user = { id: 1, name: 'John', email: 'john@example.com' };
expect(user).toMatchObject({
name: 'John',
});
});
it('expect.any - 类型匹配', () => {
expect({
id: expect.any(Number),
name: expect.any(String),
createdAt: expect.any(Date),
}).toMatchObject({
id: 1,
name: 'John',
createdAt: new Date(),
});
});
it('expect.objectContaining', () => {
const response = { success: true, data: { id: 1 }, meta: {} };
expect(response).toEqual(
expect.objectContaining({
success: true,
data: expect.objectContaining({ id: 1 }),
})
);
});
});测试组织结构
typescript
// 使用 describe 分组
describe('OrderService', () => {
// 嵌套分组
describe('createOrder', () => {
// beforeEach 在每个测试前执行
beforeEach(() => {
// 准备测试数据
});
// afterEach 在每个测试后执行
afterEach(() => {
// 清理数据
});
it('应创建正常订单', async () => {
// 测试逻辑
});
it('应拒绝无效订单', async () => {
// 测试逻辑
});
// 跳过测试
it.skip('待实现的功能', () => {});
// 只运行这个测试
it.only('调试中的测试', () => {});
});
});运行测试
json
// package.json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:ci": "jest --ci --coverage --runInBand"
}
}bash
# 运行所有测试
npm test
# 运行特定文件
npm test -- order.service.test.ts
# 运行匹配的测试
npm test -- --testNamePattern="createOrder"
# 监视模式
npm test -- --watch本节小结
Jest 是功能完整的测试框架,内置断言库、Mock 和覆盖率统计。配置时注意路径别名与 tsconfig.json 保持一致,使用 setupFilesAfterEnv 进行全局初始化。掌握常用断言方法,让测试代码简洁明了。
