Jest 入门指南:让 JavaScript 测试变得简单高效
在现代前端开发中,测试不再是可有可无的选项,而是保证代码质量的底线。而在 JavaScript 的测试领域,Jest 凭借其“开箱即用”的哲学,成为了当之无愧的行业标准。
由 Facebook 开发的 Jest,不仅适用于 React,也完美支持 Vue、Angular、Node.js 甚至纯 JavaScript 项目。
1. 为什么选择 Jest?
- 开箱即用 (Zero Config):内置了断言库、Mock 工具、测试运行器和代码覆盖率报告,不需要像以前那样组合 Mocha + Chai + Sinon。
- 极速性能:支持并行执行测试用例,并优先运行上次失败的测试,显著缩短 CI 等待时间。
- 快照测试 (Snapshot Testing):一种独特的 UI 测试方式,能够捕捉复杂对象的变更。
- 优秀的文档与社区:几乎你遇到的所有测试难题,在社区里都能找到成熟的方案。
2. 基础语法:断言与匹配器
Jest 的核心是 test 函数和 expect 断言。
test('两个数字相加', () => {
expect(1 + 2).toBe(3);
});
常用匹配器 (Matchers):
- 相等性:
toBe(引用相等),toEqual(对象值相等)。 - 真值检查:
toBeNull,toBeDefined,toBeTruthy。 - 数字对比:
toBeGreaterThan,toBeLessThanOrEqual。 - 字符串/数组:
toMatch(正则),toContain(包含)。
3. Mock 机制:测试的超能力
在单元测试中,我们经常需要隔离外部依赖(如 API 请求、数据库读写)。Jest 提供了三类强大的 Mock 工具:
3.1 jest.fn()
创建一个模拟函数,用于追踪调用情况(参数是什么?被调用了几次?)。
const mockCallback = jest.fn(x => 42 + x);
mockCallback(0);
expect(mockCallback).toHaveBeenCalledTimes(1);
expect(mockCallback).toHaveBeenCalledWith(0);
3.2 jest.spyOn()
监听某个对象的方法,而不改变其原始行为(或者部分替换它)。常用于监控 console.log 或 localStorage。
3.3 jest.mock()
直接 Mock 整个模块。这在测试组件请求 API 时非常有用:
import axios from 'axios';
jest.mock('axios');
test('应该获取用户数据', async () => {
axios.get.mockResolvedValue({ data: { name: 'Gemini' } });
// 执行调用逻辑...
});
4. 企业级实战:NestJS 与 Jest 的集成
NestJS 深度集成了 Jest,并提供了一套专门的测试工具包 @nestjs/testing。
4.1 模拟模块 (Test.createTestingModule)
在 NestJS 中,我们不需要手动 new 类,而是使用官方提供的测试模块创建器:
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
describe('UsersService', () => {
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UsersService],
}).compile();
service = module.get<UsersService>(UsersService);
});
it('应该已定义', () => {
expect(service).toBeDefined();
});
});
4.2 轻松 Mock 依赖项 (overrideProvider)
当你测试 Controller 时,通常不想让它真的去调数据库,这时可以使用覆盖机制:
const module = await Test.createTestingModule({
controllers: [UsersController],
providers: [UsersService],
})
.overrideProvider(UsersService)
.useValue({ findAll: jest.fn().mockReturnValue([{ name: 'Mock User' }]) })
.compile();
4.3 端到端 (E2E) 测试
NestJS 利用 supertest 让我们能模拟真实的 HTTP 请求:
import * as request from 'supertest';
it('/GET users', () => {
return request(app.getHttpServer())
.get('/users')
.expect(200)
.expect([{ id: 1, name: 'Gemini' }]);
});
5. 快照测试 (Snapshot Testing)
当你想确保 UI 结构或大型配置对象没有意外更改时,快照测试非常高效。
test('快照测试示例', () => {
const tree = { foo: 'bar', date: new Date() };
expect(tree).toMatchSnapshot();
});
执行后,Jest 会生成一个 .snap 文件。下次运行时,如果对象结构发生变化,Jest 会报错并询问你是否需要更新快照。
6. 测试的最佳实践
6.1 遵循 AAA 模式
- Arrange (准备):设置数据、Mock、环境变量。
- Act (执行):调用被测函数或触发交互。
- Assert (断言):验证结果是否符合预期。
6.2 保持测试独立性
使用 beforeEach 或 afterEach 来重置状态,确保每个测试用例之间没有干扰。
6.3 关注代码覆盖率
通过 --coverage 参数,Jest 可以生成详细的报告。虽然不追求 100% 的覆盖率,但关键业务逻辑必须被覆盖。
总结
Jest 将测试从一种“负担”变成了一种“享受”。它强大的工具链让你能够专注于逻辑验证,而不是配置环境。
记住:测试不是为了证明你是对的,而是为了在你犯错时及时提醒你。
现在就开始为你的项目加上第一个 expect 吧!