JavaScript 测试:单元测试、集成测试、端到端测试 — 完整指南
代码质量是任何项目成功的关键。在 JavaScript(尤其是 Node.js)领域,测试已不仅仅是良好实践,而是一种必要。我们将解析三个关键层级:单元测试、集成测试和端到端测试。您将了解它们的区别、何时使用每种类型,并通过流行工具看到实际示例。
1. 单元测试:检查隔离的模块
单元测试检查单个函数、类或模块在隔离外部依赖(数据库、API、文件系统)的情况下的工作状态。这是最快、最可靠的层级。
工具与配置
最流行的框架是 Jest。安装:
npm install --save-dev jest在 package.json 中添加脚本:
"scripts": { "test": "jest"}示例:测试折扣计算函数
创建文件 discount.js:
// discount.jsfunction applyDiscount(price, discountPercent) { if (price <= 0) throw new Error('Price must be positive'); if (discountPercent < 0 || discountPercent > 100) throw new Error('Invalid discount'); return price - (price * discountPercent) / 100;}module.exports = { applyDiscount };现在在 discount.test.js 中编写单元测试:
const { applyDiscount } = require('./discount');
describe('applyDiscount', () => { test('calculates 10% discount correctly', () => { expect(applyDiscount(100, 10)).toBe(90); });
test('throws error for negative price', () => { expect(() => applyDiscount(-10, 10)).toThrow('Price must be positive'); });
test('throws error for invalid discount percent', () => { expect(() => applyDiscount(100, 150)).toThrow('Invalid discount'); });});运行:npm test。Jest 会自动查找 *.test.js 文件。
建议:使用 mock 对象来隔离外部服务。
// 外部 API 的 Mock 示例jest.mock('./api');const api = require('./api');api.getData.mockResolvedValue({ id: 1 });2. 集成测试:检查组件间的交互
集成测试检查不同模块如何协同工作:数据库 + 业务逻辑 + HTTP 路由。它们比单元测试慢,但能提供更高的系统正确性信心。
工具:Supertest + Jest
Supertest 允许您向 Express 应用发送 HTTP 请求,而无需实际启动服务器。
npm install --save-dev supertest示例:测试 REST API
假设有一个简单的 Express 应用 app.js:
const express = require('express');const app = express();app.use(express.json());
app.post('/users', (req, res) => { const { name, email } = req.body; if (!name || !email) { return res.status(400).json({ error: 'Missing fields' }); } // 保存到数据库(简化处理) res.status(201).json({ id: 1, name, email });});
module.exports = app;集成测试 users.test.js:
const request = require('supertest');const app = require('./app');
describe('POST /users', () => { test('creates user with valid data', async () => { const response = await request(app) .post('/users') .send({ name: 'Alice', email: 'alice@test.com' }); expect(response.status).toBe(201); expect(response.body).toHaveProperty('id'); expect(response.body.name).toBe('Alice'); });
test('returns 400 for missing email', async () => { const response = await request(app) .post('/users') .send({ name: 'Bob' });```