# 集成测试最佳实践指南
## 概述
本文档提供了项目集成测试的最佳实践、模式和指南,帮助开发团队编写高质量、可维护的集成测试。
## 测试类型区分
### 单元测试 (Unit Tests)
- **范围**: 单个函数、类或组件
- **目标**: 验证独立单元的 correctness
- **位置**: `src/**/__tests__/**/*.test.{ts,tsx}`
### 集成测试 (Integration Tests)
- **范围**: 多个组件/服务协作
- **目标**: 验证模块间集成和交互
- **位置**: `src/**/__integration_tests__/**/*.integration.test.{ts,tsx}`
### E2E测试 (End-to-End Tests)
- **范围**: 完整用户流程
- **目标**: 验证端到端业务流程
- **位置**: `tests/e2e/**/*.test.{ts,tsx}`
## API 集成测试模式
### 基本结构
```typescript
describe('API Integration Tests', () => {
let app: Hono;
let apiClient: ApiClient;
beforeEach(async () => {
// 设置测试环境
app = createTestApp();
apiClient = createApiClient(app);
});
afterEach(() => {
// 清理资源
});
});
```
### 请求测试模式
```typescript
it('应该返回正确的状态码和数据', async () => {
const response = await apiClient.get('/api/endpoint');
expect(response.status).toBe(200);
expect(response.data).toMatchObject(expectedData);
});
it('应该处理错误情况', async () => {
const response = await apiClient.get('/api/invalid-endpoint');
expect(response.status).toBe(404);
expect(response.data).toHaveProperty('error');
});
```
### 认证测试模式
```typescript
it('需要认证的端点应该验证令牌', async () => {
apiClient.clearAuthToken();
const response = await apiClient.get('/api/protected');
expect(response.status).toBe(401);
});
it('有效令牌应该允许访问', async () => {
apiClient.setAuthToken('valid-token');
const response = await apiClient.get('/api/protected');
expect(response.status).toBe(200);
});
```
## React 组件集成测试模式
### 基本渲染测试
```typescript
it('应该渲染所有子组件', () => {
render(
);
expect(screen.getByText('Expected Text')).toBeInTheDocument();
expect(screen.getByRole('button')).toBeInTheDocument();
});
```
### 用户交互测试
```typescript
it('应该处理用户输入和提交', async () => {
const user = userEvent.setup();
const onSubmit = vi.fn();
render(
);
await user.type(screen.getByLabelText('Email'), 'test@example.com');
await user.click(screen.getByRole('button', { name: 'Submit' }));
expect(onSubmit).toHaveBeenCalledWith('test@example.com');
});
```
### 路由集成测试
```typescript
it('应该正确处理导航', async () => {
const user = userEvent.setup();
render(
);
await user.click(screen.getByText('Go to Settings'));
expect(screen.getByText('Settings Page')).toBeInTheDocument();
});
```
## Mock 策略
### 数据库 Mock
```typescript
// 使用内存数据库
const testDataSource = new DataSource({
type: 'better-sqlite3',
database: ':memory:',
entities: [User, Role],
synchronize: true,
});
// 使用事务回滚确保测试隔离
beforeEach(async () => {
await testDataSource.initialize();
});
afterEach(async () => {
await testDataSource.destroy();
});
```
### 服务 Mock
```typescript
// 使用 vi.mock()
vi.mock('../services/external-service', () => ({
ExternalService: {
fetchData: vi.fn().mockResolvedValue(mockData),
sendData: vi.fn().mockResolvedValue({ success: true }),
}
}));
// 或者使用自定义mock工具
import { ServiceMocks } from '../__test_utils__/service-mocks';
beforeEach(() => {
ServiceMocks.setupForSuccess();
});
```
### HTTP 请求 Mock
```typescript
// 使用 MSW (Mock Service Worker)
import { setupServer } from 'msw/node';
import { rest } from 'msw';
const server = setupServer(
rest.get('/api/users', (req, res, ctx) => {
return res(ctx.json({ users: [] }));
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
```
## 测试数据管理
### 测试数据工厂
```typescript
export function createTestUser(overrides = {}): User {
return {
id: 1,
username: 'testuser',
email: 'test@example.com',
createdAt: new Date(),
...overrides
};
}
// 使用
const adminUser = createTestUser({ role: 'admin' });
const inactiveUser = createTestUser({ active: false });
```
### 数据清理策略
```typescript
// 策略1: 事务回滚 (推荐)
describe('User API', () => {
let dataSource: DataSource;
beforeEach(async () => {
dataSource = await TestDatabase.initialize();
await dataSource.startTransaction();
});
afterEach(async () => {
await dataSource.rollbackTransaction();
await dataSource.destroy();
});
});
// 策略2: 清理数据库
afterEach(async () => {
await dataSource.getRepository(User).clear();
await dataSource.getRepository(Role).clear();
});
```
## 性能优化
### 测试执行优化
```typescript
// 共享测试资源
let sharedDataSource: DataSource;
beforeAll(async () => {
sharedDataSource = await TestDatabase.initialize();
});
afterAll(async () => {
await sharedDataSource.destroy();
});
// 使用并行测试执行
// 在 vitest.config.ts 中配置
export default defineConfig({
test: {
maxThreads: 4,
minThreads: 2,
// ...
}
});
```
### 减少不必要的操作
```typescript
// 避免在每个测试中重新初始化
beforeAll(async () => {
// 一次性初始化
});
// 使用mock代替真实操作
vi.mock('heavy-operation-module');
```
## 调试技巧
### 测试调试
```typescript
// 添加调试输出
test('debug test', async () => {
console.log('Starting test...');
// 测试代码
console.log('Test completed');
});
// 使用 --test-timeout 参数增加超时时间
// vitest --test-timeout=30000
```
### 网络请求调试
```typescript
// 记录所有API请求
beforeEach(() => {
vi.spyOn(global, 'fetch').mockImplementation(async (input, init) => {
console.log('API Request:', input, init);
return mockResponse;
});
});
```
## 常见问题解决
### 测试不稳定 (Flaky Tests)
- **原因**: 异步操作时序问题
- **解决**: 使用 `waitFor` 和适当的断言
```typescript
// 错误方式
expect(element).toBeInTheDocument(); // 可能尚未渲染
// 正确方式
await waitFor(() => {
expect(element).toBeInTheDocument();
});
```
### 内存泄漏
- **原因**: 未正确清理资源
- **解决**: 确保 afterEach/afterAll 中清理所有资源
```typescript
afterEach(async () => {
await cleanupTestResources();
vi.clearAllMocks();
vi.resetAllMocks();
});
```
### 测试执行缓慢
- **原因**: 过多的真实数据库操作
- **解决**: 使用内存数据库或更好的mock策略
## CI/CD 集成
### 测试配置
```yaml
# GitHub Actions 示例
name: Integration Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: test_db
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run test:integration
```
### 测试报告
```yaml
# 生成测试报告
- run: npm run test:integration -- --reporter=junit --outputFile=test-results.xml
- uses: actions/upload-artifact@v4
with:
name: test-results
path: test-results.xml
```
## 代码质量检查
### ESLint 规则
```javascript
// .eslintrc.js
module.exports = {
rules: {
'testing-library/await-async-utils': 'error',
'testing-library/no-await-sync-events': 'error',
'testing-library/no-debugging-utils': 'warn',
'testing-library/no-dom-import': 'error',
}
};
```
### 测试覆盖率要求
- **API 端点**: ≥ 80%
- **关键业务逻辑**: ≥ 90%
- **错误处理路径**: ≥ 70%
- **整体覆盖率**: ≥ 75%
## 附录
### 有用命令
```bash
# 运行集成测试
npm run test:integration
# 运行特定测试文件
npm run test:integration -- src/server/__integration_tests__/users.integration.test.ts
# 调试模式
npm run test:integration -- --inspect-brk
# 生成覆盖率报告
npm run test:integration -- --coverage
```
### 推荐工具
- **Vitest**: 测试框架
- **Testing Library**: React 测试工具
- **MSW**: HTTP Mock 工具
- **better-sqlite3**: 内存数据库
- **Docker**: 测试数据库容器
---
*最后更新: 2025-09-15*
*版本: 1.0*