TypeScript 高级测试:从单元测试到端到端测试
测试不仅仅是检查代码是否正常工作,更是专业 TypeScript 开发的基础。在高级水平上,我们从简单的断言转向构建隔离、快速且可靠的测试金字塔。在本文中,我们将使用 TypeScript 生态系统中的最佳工具,剖析单元测试、集成测试和端到端测试的实践方法。
1. 使用 Jest 和 TypeScript 进行高级单元测试
单元测试检查隔离的模块。在 TypeScript 中,关键挑战在于处理依赖关系、类型和异步操作。
1.1. 使用 jest.mocked 进行模拟和类型化
使用 jest.mocked 为模拟对象提供完整的类型化支持。这可以确保你不会调用不存在的方法。
import { fetchUserData } from './api';import { processUser } from './userProcessor';
jest.mock('./api');
const mockedFetchUserData = jest.mocked(fetchUserData);
describe('processUser', () => { it('应该处理用户数据', async () => { const mockUser = { id: 1, name: 'Alice', email: 'alice@test.com' }; mockedFetchUserData.mockResolvedValue(mockUser);
const result = await processUser(1);
expect(result).toEqual({ displayName: 'Alice', contact: 'alice@test.com' }); expect(mockedFetchUserData).toHaveBeenCalledWith(1); });
it('当缺少 email 时应抛出错误', async () => { mockedFetchUserData.mockResolvedValue({ id: 2, name: 'Bob', email: null }); await expect(processUser(2)).rejects.toThrow('Email is required'); });});1.2. 测试复杂类型和泛型
对于带有泛型的函数,创建辅助测试类型以避免重复。
// 代码function getFirstElement<T>(arr: T[]): T | undefined { return arr[0];}
// 测试type TestArray = number[];const array: TestArray = [10, 20, 30];
describe('getFirstElement', () => { it('应返回数组的第一个数字元素', () => { const result = getFirstElement(array); expect(result).toBe(10); // TypeScript 自动推断类型为 number });
it('对于空数组应返回 undefined', () => { const result = getFirstElement([]); expect(result).toBeUndefined(); });});2. 集成测试:数据库和 API
集成测试检查模块(控制器、服务和数据库)之间的交互。对于基于 Node.js 的 TypeScript 应用程序,Jest + Supertest + Testcontainers 组合是理想选择。
2.1. 使用 Testcontainers 配置测试数据库
Testcontainers 在 Docker 中启动 PostgreSQL,确保每个测试都有干净的环境。
import { PostgreSqlContainer, StartedPostgreSqlContainer } from '@testcontainers/postgresql';import { Client } from 'pg';
let container: StartedPostgreSqlContainer;let client: Client;
beforeAll(async () => { container = await new PostgreSqlContainer().start(); client = new Client({ connectionString: container.getConnectionUri() }); await client.connect(); await client.query(` CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) UNIQUE NOT NULL ); `);}, 30000); // 超时 30 秒用于下载镜像
afterAll(async () => { await client.end(); await container.stop();});
describe('User Repository', () => { it('应创建并返回用户', async () => { const res = await client.query( `INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *`, ['Test', 'test@example.com'] ); expect(res.rows[0].name).toBe('Test'); });});2.2. 使用 Supertest 测试 REST API
启动 Express 应用程序并进行测试