Appearance
Monorepo 测试策略
测试挑战
在 Monorepo 环境中进行测试面临独特的挑战:
- 测试范围确定: 确定需要运行哪些测试
- 依赖关系管理: 处理包之间的依赖关系
- 测试执行效率: 在大型项目中保持测试效率
- 测试隔离: 确保测试的独立性
- 测试数据管理: 在多个包间共享测试数据
测试策略设计
1. 分层测试架构
在 Monorepo 中实施分层测试策略:
├── Unit Tests (包内测试)
├── Integration Tests (包间集成)
├── E2E Tests (端到端测试)
└── Contract Tests (契约测试)
2. 影子测试(Affected Testing)
仅运行受代码更改影响的测试:
bash
# Nx 影子测试
nx affected:test --base=main --head=HEAD
# 或使用当前分支对比
nx affected:test
3. 测试并行化
利用并行执行提高测试效率:
json
// package.json
{
"scripts": {
"test:parallel": "jest --maxWorkers=50%",
"test:affected": "nx affected:test --parallel=3"
}
}
测试配置管理
1. 共享测试配置
创建基础测试配置供所有包使用:
javascript
// jest.config.base.js
module.exports = {
testEnvironment: 'node',
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts',
'!src/**/index.{js,ts}'
],
moduleNameMapper: {
'^@/utils/(.*)$': '<rootDir>/../utils/src/$1'
}
};
2. 包特定配置
每个包可以有特定的测试配置:
javascript
// packages/my-package/jest.config.js
const baseConfig = require('../../jest.config.base');
module.exports = {
...baseConfig,
name: 'my-package',
displayName: 'My Package',
testMatch: ['<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}']
};
3. 全局测试配置
在根目录管理全局测试设置:
json
// package.json (root)
{
"jest": {
"projects": [
"<rootDir>/packages/*"
],
"collectCoverage": true,
"coverageReporters": ["text", "lcov"]
}
}
依赖测试
1. 包间依赖测试
测试包之间的依赖关系:
typescript
// packages/core/src/__tests__/integration.test.ts
import { someFunction } from '@myorg/utils';
import { CoreClass } from '../core';
describe('Core-Utils integration', () => {
it('should work with utils package', () => {
const core = new CoreClass();
const result = core.useUtility(someFunction);
expect(result).toBeDefined();
});
});
2. 契约测试
确保包之间的接口兼容性:
typescript
// packages/api-contracts/src/__tests__/contract.test.ts
import { validateResponse } from '@myorg/validator';
import { apiClient } from '@myorg/api-client';
describe('API Contract', () => {
it('should return expected response format', async () => {
const response = await apiClient.getData();
const isValid = validateResponse(response);
expect(isValid).toBe(true);
});
});
3. 版本兼容性测试
测试不同版本间的兼容性:
typescript
// packages/compatibility-tests/src/version-compat.test.ts
describe('Version compatibility', () => {
it('should maintain backward compatibility', () => {
// 测试新版本是否兼容旧版本的 API
expect(new APIv2()).toImplement(APIv1Interface);
});
});
测试优化策略
1. 测试缓存
实现测试结果缓存:
yaml
# GitHub Actions 示例
- name: Cache Jest
id: cache-jest
uses: actions/cache@v3
with:
path: .cache/jest
key: ${{ runner.os }}-jest-${{ hashFiles('**/package-lock.json') }}
2. 测试分片
将大型测试套件分片执行:
bash
# Jest 分片测试
jest --shard=1/4
jest --shard=2/4
jest --shard=3/4
jest --shard=4/4
3. 选择性测试
根据更改类型运行特定测试:
json
// package.json
{
"scripts": {
"test:unit": "jest --testPathPattern=unit",
"test:integration": "jest --testPathPattern=integration",
"test:changed": "nx affected:test --target=test:unit"
}
}
CI/CD 中的测试
1. 分阶段测试
在 CI/CD 中实施分阶段测试:
yaml
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- run: pnpm install
- run: pnpm nx affected:test --target=test:unit
integration-tests:
needs: unit-tests
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x]
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: ${{ matrix.node-version }}
- run: pnpm install
- run: pnpm nx affected:test --target=test:integration
2. 测试覆盖率
维护测试覆盖率标准:
json
// package.json
{
"jest": {
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}
}
3. 性能测试
包含性能测试:
typescript
// packages/core/src/__tests__/performance.test.ts
describe('Performance tests', () => {
it('should process data within time limit', async () => {
const start = performance.now();
await processData(largeDataset);
const end = performance.now();
expect(end - start).toBeLessThan(1000); // 1秒内完成
});
});
测试工具集成
1. Nx 测试集成
利用 Nx 的测试功能:
json
// nx.json
{
"targetDefaults": {
"test": {
"dependsOn": ["^build"],
"inputs": ["default", "^default"],
"cache": true
}
}
}
2. 测试报告
生成统一的测试报告:
json
// package.json
{
"scripts": {
"test:report": "jest --coverage --coverageReporters=lcov --testResultsProcessor=jest-junit"
}
}
3. Mock 管理
在 Monorepo 中统一管理 Mock:
typescript
// packages/test-utils/src/mocks/index.ts
export { createMockUser } from './user.mock';
export { createMockAPI } from './api.mock';
export { createMockStore } from './store.mock';
测试数据管理
1. 共享测试数据
创建共享的测试数据工厂:
typescript
// packages/test-data/src/factories/index.ts
import { UserFactory } from './user.factory';
import { ProductFactory } from './product.factory';
export const TestData = {
users: UserFactory,
products: ProductFactory
};
2. 测试数据库
管理集成测试的数据库:
typescript
// packages/test-utils/src/database.ts
export class TestDatabase {
static async setup() {
// 设置测试数据库
}
static async teardown() {
// 清理测试数据库
}
}
测试最佳实践
1. 测试命名约定
使用一致的测试命名:
typescript
// packages/ui-components/src/button/__tests__/button.test.ts
describe('Button component', () => {
describe('when clicked', () => {
it('should trigger onClick handler', () => {
// 测试逻辑
});
});
describe('when disabled', () => {
it('should not respond to clicks', () => {
// 测试逻辑
});
});
});
2. 测试组织结构
按功能组织测试:
packages/
└── my-package/
├── src/
│ ├── features/
│ │ ├── auth/
│ │ │ ├── login.ts
│ │ │ └── __tests__/
│ │ │ └── login.test.ts
│ │ └── profile/
│ │ ├── profile.ts
│ │ └── __tests__/
│ │ └── profile.test.ts
3. 测试环境
配置不同的测试环境:
typescript
// packages/my-package/src/__tests__/environment.test.ts
describe('Environment-specific tests', () => {
beforeEach(() => {
process.env.NODE_ENV = 'test';
});
afterEach(() => {
jest.resetModules();
});
it('should behave differently in test environment', () => {
// 测试逻辑
});
});
质量门禁
1. 测试通过标准
设置测试通过的质量门禁:
yaml
# 在 CI 中设置质量门禁
- name: Check coverage
run: |
# 检查覆盖率是否满足要求
if [ $COVERAGE -lt 80 ]; then
echo "Coverage too low: $COVERAGE%"
exit 1
fi
2. 性能基准
设置性能基准测试:
typescript
// packages/performance-tests/src/benchmarks.test.ts
const PERFORMANCE_THRESHOLD = 1000; // 1秒
describe('Performance benchmarks', () => {
it('should not exceed performance threshold', async () => {
const startTime = Date.now();
await runBenchmark();
const duration = Date.now() - startTime;
expect(duration).toBeLessThan(PERFORMANCE_THRESHOLD);
});
});
总结
Monorepo 中的测试策略需要考虑包之间的依赖关系、测试执行效率和维护成本。关键要点包括:
- 分层测试: 实施单元、集成和端到端测试的分层策略
- 影子测试: 仅运行受更改影响的测试以提高效率
- 共享配置: 统一管理测试配置和工具
- 依赖测试: 确保包间依赖的正确性
- 性能优化: 通过缓存、并行化和分片优化测试执行
- 质量门禁: 设置自动化质量检查标准
通过实施这些测试策略,可以确保 Monorepo 项目的质量和稳定性。