测试最佳实践概述
测试是保障代码质量的重要手段,Claude Code 可以帮你高效编写测试。
测试金字塔
单元测试 → 集成测试 → E2E 测试,底层测试越多,上层测试越少。
测试类型
测试金字塔
测试金字塔:
/\
/E2E\ 少量 E2E 测试
/------\
/集成测试\ 中等集成测试
/----------\
/ 单元测试 \ 大量单元测试
/------------\
比例:70% 单元测试,20% 集成测试,10% E2E 测试
各层测试特点
| 类型 | 测试对象 | 特点 | 速度 |
|------|----------|------|------|
| 单元测试 | 函数/方法 | 隔离、快速 | < 1ms |
| 集成测试 | 模块交互 | 涉及数据库 | < 100ms |
| E2E 测试 | 完整流程 | 模拟用户 | < 1s |
Claude Code 测试生成
生成单元测试
bash
# Claude Code 生成单元测试
claude --generate-tests --unit src/utils/validator.ts输出示例
typescript
// src/utils/validator.test.ts
import { describe, it, expect } from 'vitest';
import { validateEmail, validatePassword } from './validator';
describe('validateEmail', () => {
it('应该接受有效的邮箱格式', () => {
expect(validateEmail('test@example.com')).toBe(true);
expect(validateEmail('user.name@domain.co')).toBe(true);
});
it('应该拒绝无效的邮箱格式', () => {
expect(validateEmail('invalid')).toBe(false);
expect(validateEmail('@domain.com')).toBe(false);
expect(validateEmail('user@')).toBe(false);
});
});
describe('validatePassword', () => {
it('应该接受符合要求的密码', () => {
expect(validatePassword('Password123!')).toBe(true);
expect(validatePassword('MyP@ssw0rd')).toBe(true);
});
it('应该拒绝太短的密码', () => {
expect(validatePassword('Pas1!')).toBe(false);
expect(validatePassword('Ab1!')).toBe(false);
});
it('应该拒绝没有特殊字符的密码', () => {
expect(validatePassword('Password123')).toBe(false);
});
});TDD 开发模式
TDD 流程
TDD 红-绿-重构循环:
1. 红(Red) → 写一个失败的测试
2. 绿(Green)→ 写最简单的代码让测试通过
3. 重构(Refactor)→ 优化代码,保持测试通过
TDD 示例
bash
# TDD 开发一个计算器
claude --tdd --create calculator
# 输出示例
## 1. 红:先写测试
describe('Calculator', () => {
it('应该正确执行加法', () => {
const calc = new Calculator();
expect(calc.add(2, 3)).toBe(5);
});
it('应该正确执行减法', () => {
const calc = new Calculator();
expect(calc.subtract(5, 3)).toBe(2);
});
});
## 2. 绿:写最简单的实现
class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
}
## 3. 重构:优化代码
class Calculator {
operate(a, b, operator) {
const operations = {
'+': (a, b) => a + b,
'-': (a, b) => a - b,
};
return operations[operator]?.(a, b) ?? NaN;
}
}测试覆盖
覆盖报告
bash
# 生成测试覆盖报告
claude --coverage --report
# 输出示例
## 测试覆盖报告
| 文件 | 行覆盖 | 分支覆盖 | 函数覆盖 |
|------|--------|----------|----------|
| src/utils/validator.ts | 95% | 88% | 100% |
| src/utils/format.ts | 82% | 65% | 100% |
| src/services/user.ts | 70% | 55% | 100% |
## 未覆盖的代码
src/utils/format.ts:24-26
src/services/user.ts:45-48提高覆盖率的策略
typescript
// 1. 测试边界条件
it('应该处理空数组', () => {
expect(sum([])).toBe(0);
});
it('应该处理极大数', () => {
expect(sum([Number.MAX_SAFE_INTEGER, 1])).toBe(Number.MAX_SAFE_INTEGER);
});
// 2. 测试错误情况
it('应该抛出预期错误', () => {
expect(() => parseJSON('invalid')).toThrow(SyntaxError);
});
// 3. 测试 null/undefined
it('应该处理 null 输入', () => {
expect(processValue(null)).toBeNull();
});Mock 与 Stub
Jest Mock
typescript
// Mock 函数
const mockFn = vi.fn();
mockFn.mockReturnValue('result');
mockFn.mockImplementation((input) => input * 2);
// Mock 模块
vi.mock('./api', () => ({
fetchUser: vi.fn(),
}));
// Mock 定时器
vi.useFakeTimers();
vi.advanceTimersByTime(1000);Stub 示例
typescript
// Stub 服务依赖
const mockUserService = {
getUserById: vi.fn().mockResolvedValue({ id: '1', name: 'Test' }),
createUser: vi.fn().mockResolvedValue({ id: '2' }),
};
const service = new UserService(mockUserService);集成测试
API 集成测试
typescript
// tests/api/user.api.test.ts
import { describe, it, expect, beforeAll } from 'vitest';
import supertest from 'supertest';
import { app } from '../../app';
describe('POST /api/users', () => {
beforeAll(async () => {
await resetDatabase();
});
it('应该成功创建用户', async () => {
const response = await supertest(app)
.post('/api/users')
.send({
username: 'testuser',
email: 'test@example.com',
password: 'Password123!',
})
.expect(201);
expect(response.body.success).toBe(true);
expect(response.body.data.username).toBe('testuser');
});
it('应该验证邮箱格式', async () => {
await supertest(app)
.post('/api/users')
.send({
username: 'testuser',
email: 'invalid-email',
password: 'Password123!',
})
.expect(400);
});
});持续集成
CI 测试配置
yaml
# .github/workflows/test.yml
name: Test
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run lint
run: npm run lint
- name: Run tests
run: npm run test:coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info测试最佳实践清单
✅ 测试应该:
- 快速(毫秒级)
- 独立(不依赖其他测试)
- 可重复(结果一致)
- 真实(测试真实行为)
- 清晰(测试名称描述意图)
❌ 测试不应该:
- 依赖执行顺序
- 共享可变状态
- 访问网络或数据库(集成测试除外)
- 测试实现细节而非行为
测试价值
好的测试让你有信心重构,有信心发布,有信心创新。
总结
Claude Code 测试最佳实践:
- 测试金字塔:底层测试多,上层测试少
- TDD:红-绿-重构循环
- 覆盖率:目标 80%+ 行覆盖
- Mock:隔离依赖
- CI:自动化测试
- 最佳实践:快速、独立、可重复
记住:没有测试的代码是有缺陷的代码。