核心区别在于测试边界,而不是"调用方法的数量"
测试单个模块/函数在隔离环境中的行为。外部依赖(数据库、网络、其他服务)全部用 mock 替代。
// 测试 discountCalculator 的纯逻辑,数据库被 mock 掉 jest.mock('../database/pool'); test('10% discount on $100 = $90', () => { expect(calculateDiscount(100, 0.1)).toBe(90); });
关键特征:快、确定性强、失败时能精确定位到哪个函数出了问题。
测试多个真实组件协作时的行为。至少有一个外部依赖是真实的(真实数据库、真实 HTTP 调用等)。
// 真实连接数据库,测试 API → 路由 → 中间件 → 数据库 的完整链路 test('POST /api/appointments creates record in DB', async () => { const res = await request(app) .post('/api/appointments') .send({ /* ... */ }); expect(res.status).toBe(201); // 验证数据库中确实写入了记录 const row = await db.query( 'SELECT * FROM appointments WHERE id = $1', [res.body.id] ); expect(row.rows).toHaveLength(1); });
关键特征:慢、可能因环境问题失败,但能发现组件之间的接口不匹配、数据格式错误、SQL 语句问题等单元测试发现不了的 bug。
| 单元测试 | 集成测试 | |
|---|---|---|
| 外部依赖 | 全部 mock | 至少部分真实 |
| 测试目标 | 单个函数/类的逻辑正确性 | 组件之间的协作是否正常 |
| 典型发现的 bug | 算法错误、边界条件 | SQL 写错、接口参数不匹配、中间件顺序错误 |
| 速度 | 毫秒级 | 秒级(涉及数据库/网络) |
| 失败定位 | 精确到函数 | 只知道某条链路断了,需要排查 |
"连续调用多个方法"本身不构成集成测试——如果那些方法的依赖都被 mock 了,它仍然是单元测试。
是否使用真实的外部依赖才是分界线。
| 类型 | 目录 | 特征 |
|---|---|---|
| 单元测试 | backend/tests/unit/ |
Mock 了数据库(jest.mock),测试纯业务逻辑 |
| 集成测试 | backend/tests/integration/ |
真实连接 PostgreSQL,测试完整请求链路 |
| API 测试 | backend/tests/api/ |
Mock 了数据库(supertest / mock req/res),测试 HTTP 请求/响应链路和状态码 |