测试覆盖率与 CI 集成
AI 生成代码的覆盖率标准
传统项目通常设定 70-80% 的覆盖率门槛。对于 AI 辅助开发的项目,这个标准应该更高。
原因:
- AI 生成测试的成本极低,没有理由接受低覆盖率
- AI 可能生成逻辑正确但有细微 bug 的代码,更高覆盖率能捕获更多问题
- AI 代码的可信度需要测试来背书,覆盖率是最直观的质量信号
推荐覆盖率目标
| 项目类型 | 语句覆盖率 | 分支覆盖率 | 函数覆盖率 |
|---|---|---|---|
| 业务核心模块 | 90%+ | 85%+ | 95%+ |
| 工具函数库 | 95%+ | 90%+ | 100% |
| API 路由层 | 85%+ | 80%+ | 90%+ |
| UI 组件 | 70%+ | 65%+ | 80%+ |
| 整体项目 | 80%+ | 75%+ | 85%+ |
覆盖率配置
Vitest 覆盖率配置
typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
coverage: {
provider: 'v8', // 或 'istanbul'
reporter: ['text', 'html', 'lcov', 'json-summary'],
reportsDirectory: './coverage',
// 覆盖率门槛
thresholds: {
statements: 80,
branches: 75,
functions: 85,
lines: 80,
},
// 排除不需要覆盖的文件
exclude: [
'node_modules/**',
'dist/**',
'**/*.d.ts',
'**/*.config.*',
'**/test-helpers/**',
'coverage/**',
'.vitepress/**',
],
// 包含的文件范围
include: ['src/**/*.{ts,tsx}'],
},
},
})运行覆盖率检查:
bash
# 运行测试并生成覆盖率报告
npx vitest run --coverage
# 如果覆盖率低于阈值,命令会以非零状态退出CI 流水线设计
推荐的 CI 检查顺序
代码推送 / PR 创建
↓
1. 代码检查(Lint + TypeScript 类型检查) ← 最快,先失败先知道
↓
2. 单元测试 + 覆盖率检查 ← 核心质量门槛
↓
3. 集成测试 ← 需要服务依赖
↓
4. E2E 测试(可选,仅 main 分支) ← 最慢,按需运行
↓
✓ 全部通过 → 允许合并GitHub Actions 配置
基础 CI 配置
yaml
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
# ─── 代码质量检查 ───────────────────────────────────────────
lint:
name: Lint & Type Check
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 ESLint
run: npm run lint
- name: TypeScript type check
run: npm run type-check
# ─── 单元测试 + 覆盖率 ──────────────────────────────────────
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
needs: lint
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 unit tests with coverage
run: npm run test:coverage
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
retention-days: 7
# 在 PR 评论中显示覆盖率摘要
- name: Coverage comment on PR
if: github.event_name == 'pull_request'
uses: davelosert/vitest-coverage-report-action@v2
with:
json-summary-path: coverage/coverage-summary.json
# ─── 集成测试 ───────────────────────────────────────────────
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
needs: unit-tests
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
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 database migrations
run: npm run db:migrate
env:
DATABASE_URL: postgresql://test:test@localhost:5432/testdb
- name: Run integration tests
run: npm run test:integration
env:
DATABASE_URL: postgresql://test:test@localhost:5432/testdb
NODE_ENV: testE2E 测试配置(仅 main 分支)
yaml
# ─── E2E 测试(仅在 main 分支运行)─────────────────────────
e2e-tests:
name: E2E Tests
runs-on: ubuntu-latest
needs: integration-tests
if: github.ref == 'refs/heads/main'
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: Install Playwright browsers
run: npx playwright install --with-deps chromium
- name: Build application
run: npm run build
- name: Start application
run: npm run start &
env:
NODE_ENV: test
- name: Wait for server
run: npx wait-on http://localhost:3000 --timeout 30000
- name: Run E2E tests
run: npm run test:e2e
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 3质量门禁设置
分支保护规则
在 GitHub 仓库设置中配置(Settings → Branches → Branch protection rules):
保护 main 分支:
✓ Require status checks to pass before merging
- CI / Lint & Type Check
- CI / Unit Tests
- CI / Integration Tests
✓ Require branches to be up to date before merging
✓ Require pull request reviews before merging (1 reviewer)
✓ Dismiss stale pull request approvals when new commits are pushedpackage.json 脚本配置
json
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage",
"test:integration": "vitest run --config vitest.integration.config.ts",
"test:e2e": "playwright test",
"lint": "eslint src --ext .ts,.tsx",
"type-check": "tsc --noEmit"
}
}覆盖率报告阅读指南
运行 npm run test:coverage 后,终端输出示例:
% Coverage report from v8
-------------------|---------|----------|---------|---------|
File | % Stmts | % Branch | % Funcs | % Lines |
-------------------|---------|----------|---------|---------|
All files | 84.23 | 79.41 | 88.57 | 84.23 |
src/ | | | | |
discount.ts | 100 | 100 | 100 | 100 |
order.ts | 91.67 | 83.33 | 100 | 91.67 |
user.service.ts | 72.34 | 65.00 | 76.92 | 72.34 | ← 需要关注
-------------------|---------|----------|---------|---------|如何处理低覆盖率文件:
- 查看
coverage/index.html找到未覆盖的具体行 - 判断:是真的缺少测试,还是死代码?
- 如果是死代码,删除它;如果是缺少测试,补充
不必追求 100% 覆盖率的情况:
- 错误处理的 fallback 分支(极难触发的防御性代码)
- 第三方库的类型定义文件
- 框架生成的脚手架代码
本地开发工作流建议
bash
# 监视模式:修改代码时自动重跑相关测试
npx vitest
# 提交前手动验证
npm run test:coverage && npm run lint && npm run type-check
# 使用 git hook 自动化(pre-commit)
# 安装 husky
npm install --save-dev husky
npx husky init
# .husky/pre-commit
npm run lint && npm run type-check
# .husky/pre-push
npm run test:coverage总结
CI 集成把"测试是否通过"从个人习惯变成了团队硬性约束。对于 AI 辅助开发,这一点尤为重要:AI 生成的代码需要自动化验证作为最后一道防线。推荐的起步配置是:PR 必须通过单元测试 + 覆盖率检查,后续再逐步加入集成测试和 E2E 测试。