# 集成测试最佳实践指南 ## 概述 本文档提供了项目集成测试的最佳实践、模式和指南,帮助开发团队编写高质量、可维护的集成测试。 ## 测试类型区分 ### 单元测试 (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: postgres: image: postgres:15 env: POSTGRES_PASSWORD: postgres POSTGRES_DB: test_db ports: - 5432:5432 options: >- --health-cmd="pg_isready" --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*