引言:为什么要测试 TypeScript 代码?
TypeScript 是 JavaScript 的超集,增加了静态类型检查。然而,类型并不能保证应用程序逻辑的正确性。在 TypeScript 中进行测试不仅仅是一种“良好实践”,更是维护代码质量、防止回归和加速开发的必要条件。
在本文中,我们将探讨三个主要的测试层级:unit(单元测试)、integration(集成测试)和 e2e(端到端测试)。您将了解使用哪些工具,看到代码示例,并理解如何为您的 TypeScript 项目构建一个平衡的测试策略。
1. TypeScript 中的单元测试
什么是单元测试?
单元测试用于验证独立的模块或函数。它们执行速度快,且不需要外部依赖(数据库、网络、文件系统)。主要目标是确保每一小段代码都能正确运行。
工具:Jest + ts-jest
TypeScript 中最流行的单元测试框架是 Jest 配合转译器 ts-jest。它支持 mock、spy、覆盖率报告,并且与 TypeScript 集成良好。
单元测试示例
假设我们有一个计算折扣的函数:
// discount.tsexport function calculateDiscount(price: number, discountPercent: number): number { if (price < 0 || discountPercent < 0 || discountPercent > 100) { throw new Error('Invalid input'); } return price * (1 - discountPercent / 100);}编写测试:
// discount.test.tsimport { calculateDiscount } from './discount';
describe('calculateDiscount', () => { it('should apply 10% discount correctly', () => { expect(calculateDiscount(1000, 10)).toBe(900); });
it('should throw error for negative price', () => { expect(() => calculateDiscount(-100, 10)).toThrow('Invalid input'); });
it('should handle 0% discount', () => { expect(calculateDiscount(500, 0)).toBe(500); });});运行:npx jest。Jest 会自动查找所有 *.test.ts 文件。
单元测试的最佳实践
- 隔离依赖——对访问数据库或 API 的模块使用 mock(jest.mock)。
- 测试边界情况——空数组、null、负数。
- 遵循 FIRST 原则——Fast(快速)、Isolated(隔离)、Repeatable(可重复)、Self-validating(自我验证)、Timely(及时)。
- 使用 describe/it——使测试结构清晰可读。
2. 集成测试
集成测试验证什么?
集成测试用于验证多个模块之间的交互:控制器 + 服务 + 数据库,或者 API 端点 + 中间件。它们比单元测试慢,但能更可靠地确保组件之间正确协作。
工具:Supertest + Jest + 内存数据库
测试 Express/Node.js API 时,通常使用 Supertest(HTTP 断言)配合 Jest。数据库可以以内存方式启动(例如 mongodb-memory-server 或 sqlite3)。
Express API 的集成测试示例
假设我们有一个简单的用户管理 REST API:
// app.tsimport express from 'express';import { userRouter } from './routes/userRoutes';
const app = express();app.use(express.json());app.use('/api/users', userRouter);
export { app };使用 Supertest 进行测试:
// user.integration.test.tsimport request from 'supertest';import { app } from '../app';
describe('POST /api/users', () => { it('should create a new user and return 201', async () => {```