9.5.4 测试覆盖了多少代码——测试覆盖率:代码覆盖率阈值设置
覆盖率不是目标,而是指标——80% 的覆盖率比 100% 的虚假覆盖更有价值。
覆盖率类型
Jest 覆盖率配置
typescript
// jest.config.ts
import type { Config } from 'jest';
const config: Config = {
collectCoverage: true,
coverageDirectory: 'coverage',
coverageProvider: 'v8',
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/**/*.test.{ts,tsx}',
'!src/**/*.stories.{ts,tsx}',
'!src/types/**/*',
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
// 关键模块更高要求
'./src/services/': {
branches: 90,
functions: 90,
lines: 90,
statements: 90,
},
},
coverageReporters: ['text', 'lcov', 'html'],
};
export default config;CI 配置
yaml
# .github/workflows/ci.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run tests with coverage
run: npm run test:ci
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
fail_ci_if_error: true
verbose: truepackage.json 脚本
json
{
"scripts": {
"test": "jest",
"test:ci": "jest --ci --coverage --maxWorkers=2",
"test:coverage": "jest --coverage --coverageReporters=text"
}
}Codecov 配置
yaml
# codecov.yml
coverage:
status:
project:
default:
target: 80%
threshold: 2%
patch:
default:
target: 80%
comment:
layout: "reach,diff,flags,tree"
behavior: default
require_changes: true
ignore:
- "**/*.test.ts"
- "**/*.d.ts"
- "src/types/**"合理的覆盖率目标
| 代码类型 | 建议覆盖率 | 原因 |
|---|---|---|
| 业务服务 | 90%+ | 核心逻辑必须充分测试 |
| API 路由 | 80%+ | 接口契约需要验证 |
| 工具函数 | 95%+ | 纯函数容易测试 |
| UI 组件 | 60-70% | 视觉测试更重要 |
| 配置文件 | 忽略 | 无需测试 |
避免虚假覆盖
typescript
// ❌ 虚假覆盖:只调用不验证
it('should create user', async () => {
await userService.create({ email: 'test@example.com' });
// 没有断言,覆盖率高但没价值
});
// ✅ 真实覆盖:验证行为
it('should create user with hashed password', async () => {
const user = await userService.create({
email: 'test@example.com',
password: 'secret',
});
expect(user.email).toBe('test@example.com');
expect(user.password).not.toBe('secret');
expect(await bcrypt.compare('secret', user.password)).toBe(true);
});覆盖率报告分析
bash
# 查看覆盖率报告
npm run test:coverage
# 打开 HTML 报告
open coverage/lcov-report/index.html-------------------|---------|----------|---------|---------|
File | % Stmts | % Branch | % Funcs | % Lines |
-------------------|---------|----------|---------|---------|
All files | 85.23 | 78.45 | 82.14 | 85.67 |
services/ | 92.45 | 88.23 | 90.12 | 92.78 |
user.service.ts | 95.12 | 92.34 | 94.56 | 95.45 |
order.service.ts | 89.78 | 84.12 | 85.67 | 90.12 |
lib/ | 78.34 | 68.45 | 74.23 | 78.89 |
-------------------|---------|----------|---------|---------|渐进式提升
yaml
# codecov.yml
coverage:
status:
project:
default:
# 只要求不降低覆盖率
target: auto
threshold: 1%
patch:
default:
# 新代码必须 80% 覆盖
target: 80%本节小结
覆盖率是代码质量的参考指标,不是目标。设置合理的阈值(80% 是常见选择),关注新增代码的覆盖率,避免虚假覆盖。使用 Codecov 集成到 PR 流程,让覆盖率变化可视化。
