QA & Test Engineering Command Center
完整的质量保证系统 — 从测试策略到自动化框架、覆盖率分析和发布准备。适用于任何技术栈,任何团队规模。
何时使用
- 为新功能或项目规划测试策略
- 编写单元、集成或E2E测试
- 审查测试质量和覆盖率差距
- 设置测试自动化和CI/CD质量门
- 性能测试和负载分析
- 安全测试清单
- 缺陷分类和管理
- 发布准备评估
第1阶段:测试策略
策略简报
在编写任何测试之前,定义策略:
# test-strategy.yaml
project: "[名称]"
scope: "[功能/模块/完整产品]"
risk_level: high | medium | low
stack:
language: "[TypeScript/Python/Java/Go]"
framework: "[React/Express/Django/Spring]"
test_runner: "[Jest/Vitest/pytest/JUnit/Go test]"
e2e_tool: "[Playwright/Cypress/Selenium]"
# 我们测试什么?
test_scope:
- area: "[例如,认证模块]"
risk: high
test_types: [unit, integration, e2e]
priority: 1
- area: "[例如,设置页面]"
risk: low
test_types: [unit]
priority: 3
# 什么不在范围内(为什么)
exclusions:
- "[例如,第三方小部件 — 由供应商覆盖]"
# 质量目标
targets:
line_coverage: 80
branch_coverage: 70
critical_path_coverage: 100
max_flaky_rate: 2%
max_test_duration_unit: 10ms
max_test_duration_integration: 500ms
max_test_duration_e2e: 30s
基于风险的测试分配
不是所有内容都需要相同的测试深度。使用风险矩阵:
| 风险等级 | 单元测试 | 集成 | E2E | 手动/探索性 |
|---|---|---|---|---|
| 关键 (支付、认证、数据丢失) | 95%+ 覆盖率 | 全API覆盖 | 快乐+错误路径 | 探索性会议 |
| 高 (核心功能、面向用户) | 85%+ 覆盖率 | 关键集成 | 快乐路径 | 抽查 |
| 中 (次要功能) | 70%+ 覆盖率 | 仅关键路径 | 仅冒烟 | 发布时 |
| 低 (管理员、内部工具) | 50%+ 覆盖率 | 无 | 无 | 无 |
测试金字塔
遵循金字塔 —— 而不是冰淇淋锥:
/ E2E \ ← 少量(5-10%) — 慢,昂贵,脆弱
/ Integr. \ ← 一些(15-25%) — API合同,数据库查询
/ Unit \ ← 许多(65-80%) — 快速,隔离,便宜
反模式:冰淇淋锥(主要是E2E,很少单元测试)= 慢CI,不稳定构建,昂贵维护。
决策规则: 这可以在更低级别测试吗?→ 在那里测试。
第2阶段:单元测试
好的单元测试的解剖学
每个单元测试遵循AAA(安排-行动-断言):
1. 安排 — 设置测试数据,模拟,状态
2. 行动 — 调用被测试的函数/方法
3. 断言 — 验证输出是否符合预期
单元测试清单(每个函数)
对于每个函数/方法,验证:
- [ ] 快乐路径 — 预期输入 → 预期输出
- [ ] 边缘情况 — 空输入,null/undefined,零,最大值
- [ ] 边界值 — 误差补偿一,最小-1,最大+1
- [ ] 错误处理 — 无效输入 → 正确错误抛出
- [ ] 返回类型 — 正确类型,形状,结构
- [ ] 副作用 — 它是否修改了它不应该的状态?
- [ ] 幂等性 — 两次调用结果相同?
要模拟什么(和不要模拟什么)
模拟这些:
- 外部API(HTTP调用,第三方服务)
- 数据库查询(仅在单元测试中)
- 文件系统操作
- 日期/时间(使用假定时器)
- 随机数生成器
- 环境变量
不要模拟这些:
- 被测试的函数本身
- 纯实用程序函数(直接测试它们)
- 数据转换
- 简单的值对象
模拟经验法则: 如果移除模拟会使测试击中网络,文件系统或数据库 → 模拟它。否则 → 不要。
测试命名约定
使用模式:[unit] [场景] [预期结果]
示例:
calculateTotal returns 0 for empty cartvalidateEmail throws for missing @ symbolparseDate handles ISO 8601 with timezone offset
覆盖率分析
重要的指标:
| 指标 | 目标 | 为什么 |
|---|---|---|
| 行覆盖率 | 80%+ | 基本完整性 |
| 分支覆盖率 | 70%+ | 捕获错过的if/else路径 |
| 函数覆盖率 | 90%+ | 确保所有函数都被测试 |
| 关键路径覆盖率 | 100% | 商业关键代码完全验证 |
避免的覆盖率陷阱:
- 100%行覆盖率 ≠ 好的测试(断言比行数更重要)
- 生成代码的覆盖率膨胀数字
- 琐碎的getter/setter填充覆盖率而无价值
- 覆盖率应该随时间增加,从不减少
第3阶段:集成测试
集成测试覆盖什么
集成测试验证组件一起工作:
- API端点 → 中间件 → 处理程序 → 数据库 → 响应
- 服务A调用服务B并处理响应
- 消息队列生产者 → 消费者 → 副作用
- 认证流程:登录 → 令牌 → 认证请求
集成测试模式
模式1:API合同测试
1. 启动测试服务器(或使用supertest/httptest)
2. 发送具有特定有效负载的HTTP请求
3. 断言:状态码,响应体形状,标题
4. 断言:数据库状态正确更改
5. 断言:触发了副作用(电子邮件,事件)
模式2:数据库集成
1. 启动测试数据库(SQLite内存中或测试容器)
2. 运行迁移
3. 种子测试数据
4. 执行查询/操作
5. 断言:数据符合预期
6. 拆除(截断或回滚事务)
模式3:外部服务
1. 记录真实的API响应(VCR/nock/wiremock)
2. 在测试中重放记录的响应
3. 断言:你的代码正确处理响应
4. 还测试:超时,500错误,格式错误的响应
集成测试清单
- [ ] 快乐路径 — 完整的流程端到端工作
- [ ] 认证 — 未认证返回401,错误角色返回403
- [ ] 验证 — 错误的有效载荷返回400带错误详情
- [ ] 未找到 — 缺少资源返回404
- [ ] 冲突 — 重复创建返回409
- [ ] 速率限制 — 过多请求返回429
- [ ] 数据库约束 — 唯一违规,外键
- [ ] 并发 — 两个同时写入不会损坏数据
- [ ] 超时处理 — 外部服务超时 → 优雅回退
第4阶段:端到端(E2E)测试
E2E策略
E2E测试验证完整的用户旅程。它们很昂贵 — 要策略性:
测试这些E2E:
- 用户注册 → 电子邮件验证 → 第一次登录
- 购买流程 → 支付 → 确认
- 关键业务工作流程(那些赚钱的)
- 跨浏览器/设备冒烟测试
不要测试这些E2E:
- 单个表单验证(单元测试)
- API错误处理(集成测试)
- 边缘情况(较低级别的测试)
- 视觉样式(视觉回归工具)
E2E测试模板
test_name: "[用户旅程名称]"
preconditions:
- "[用户已登录]"
- "[产品存在于目录中]"
steps:
- action: "导航到 /products"
verify: "产品列表可见"
- action: "点击产品A上的'添加到购物车'"
verify: "购物车徽章显示1"
- action: "点击'结账'"
verify: "显示结账表单"
- action: "填写支付详情并提交"
verify: "带有订单ID的订单确认页面"
postconditions:
- "数据库中存在状态为'已支付'的订单"
- "发送了确认电子邮件"
max_duration: 30s
易变测试管理
易变测试是CI杀手。处理它们:
易变测试分类:
- 识别 — 在10+运行中跟踪测试通过率
- 分类 — 为什么它易变?
- 定时/竞态条件 → 添加显式等待,不睡觉()
- 测试数据依赖性 → 每次运行隔离测试数据
- 外部服务 → 模拟它或使用测试容器
- 浏览器渲染 → 使用可见性检查,不延迟
- 隔离 — 移动到@flaky套件,单独运行
- 修复或删除 — 未修复的易变测试2周 → 删除它
易变率目标: < 2%的总测试运行
第5阶段:性能测试
性能测试类型
| 类型 | 目的 | 何时 |
|---|---|---|
| 负载测试 | 正常流量处理 | 每次发布前 |
| 压力测试 | 寻找断裂点 | 季度或扩展前 |
| 尖峰测试 | 突然流量激增 | 营销活动前 |
| 浸泡测试 | 随时间的内存泄漏 | 每月或主要更改后 |
| 容量测试 | 最大用户/吞吐量 | 基础设施规划 |
性能测试计划
test_name: "[API/页面]负载测试"
target: "[URL或端点]"
baseline:
p50_response: "[当前p50 ms]"
p95_response: "[当前p95 ms]"
p99_response: "[当前p99 ms]"
error_rate: "[当前%]"
scenarios:
- name: "正常负载"
vus: 50 # 虚拟用户
duration: 5m
ramp_up: 30s
thresholds:
p95_response: "< 500ms"
error_rate: "< 1%"
- name: "高峰负载"
vus: 200
duration: 10m
ramp_up: 1m
thresholds:
p95_response: "< 2000ms"
error_rate: "< 5%"
- name: "压力测试"
vus: 500
duration: 5m
ramp_up: 2m
# 找到断裂点 — 无阈值,观察,
性能指标仪表板
每个端点跟踪这些:
| 指标 | 绿色 | 黄色 | 红色 |
|---|---|---|---|
| p50响应 | < 200ms | 200-500ms | > 500ms |
| p95响应 | < 500ms | 500ms-2s | > 2s |
| p99响应 | < 1s | 1-5s | > 5s |
| 错误率 | < 0.1% | 0.1-1% | > 1% |
| 吞吐量 | > 基线 | 80-100%基线 | < 80% |
| CPU使用率 | < 60% | 60-80% | > 80% |
| 内存使用率 | < 70% | 70-85% | > 85% |
| DB查询时间 | < 50ms平均 | 50-200ms | > 200ms |
常见性能修复
| 症状 | 可能的原因 | 修复 |
|---|---|---|
| 慢API响应 | N+1查询 | 批量/联接查询 |
| 内存攀升 | 对象保留 | 配置堆,修复泄漏 |
| 超时峰值 | 连接池耗尽 | 增加池,添加排队 |
| 慢页面加载 | 大捆绑 | 代码分割,延迟加载 |
| DB瓶颈 | 缺少索引 | 在WHERE/JOIN列上添加索引 |
| 高CPU | 同步计算 | 移动到工作器/队列 |
第6阶段:安全测试
安全测试清单
每个功能/发布运行这些:
认证与授权:
- [ ] 密码使用bcrypt/argon2散列(不使用MD5/SHA1)
- [ ] 会话令牌随机,足够长度(128+位)
- [ ] JWT令牌有短过期(15分钟访问,7天刷新)
- [ ] 登录失败率限制(5次尝试 → 锁定)
- [ ] 密码重置令牌过期(最多1小时)
- [ ] 基于角色的访问在服务器端强制执行(不仅仅是UI)
- [ ] 无法通过更改URL中的ID来访问其他用户的数据
输入验证:
- [ ] SQL注入 — 到处参数化查询
- [ ] XSS — 输出编码,CSP头
- [ ] CSRF — 状态改变请求的令牌
- [ ] 路径遍历 — 验证文件路径,没有
../ - [ ] 命令注入 — 永远不要将用户输入传递给shell
- [ ] 文件上传 — 验证类型,大小,扫描恶意软件
- [ ] JSON/XML解析 — 深度限制,实体扩展禁用
数据保护:
- [ ] 到处HTTPS(HSTS头)
- [ ] 敏感数据在休息时加密
- [ ] PII不记录(在日志输出中掩码)
- [ ] API密钥不在客户端代码中
- [ ] CORS配置正确(不是
*) - [ ] 设置安全头(X-Frame-Options,X-Content-Type-Options)
基础设施:
- [ ] 依赖项扫描CVE(npm审计/pip审计)
- [ ] 扫描Docker映像(Trivy/Snyk)
- [ ] 秘密不在代码/环境文件中(使用vault)
- [ ] 错误消息不泄露内部信息
- [ ] 管理端点在VPN/IP允许列表后面
OWASP Top 10快速参考
| # | 漏洞 | 测试 |
|---|---|---|
| A01 | 访问控制破坏 | 访问其他用户资源,绕过角色检查 |
| A02 | 加密失败 | 弱散列,明文秘密,过期证书 |
| A03 | 注入 | SQL,XSS,命令,LDAP注入 |
| A04 | 不安全设计 | 业务逻辑缺陷,缺少速率限制 |
| A05 | 安全配置错误 | 默认凭据,详细错误,开放端口 |
| A06 | 易损组件 | 过时的依赖项,已知CVE |
| A07 | 认证失败 | 暴力破解,弱密码,会话固定 |
| A08 | 数据完整性失败 | 未签名更新,CI/CD管道注入 |
| A09 | 日志失败 | 缺少审计日志,无违规警报 |
| A10 | SSRF | 通过用户控制的URL访问内部网络 |
第7阶段:缺陷分类与管理
缺陷报告模板
bug_id: "[自动或手动]"
title: "[缺陷的简短描述]"
severity: P0-critical | P1-high | P2-medium | P3-low
reporter: "[名称]"
date: "[YYYY-MM-DD]"
environment:
os: "[OS + version]"
browser: "[Browser + version]"
app_version: "[version/commit]"
steps_to_reproduce:
1. "[步骤1]"
2. "[步骤2]"
3. "[步骤3]"
expected_result: "[应该发生什么]"
actual_result: "[实际发生了什么]"
frequency: "always | intermittent | once"
screenshots: "[链接]"
logs: "[相关日志输出]"
严重性分类
| 级别 | 定义 | SLA | 示例 |
|---|---|---|---|
| P0关键 | 系统关闭,数据丢失,安全漏洞 | 4小时内修复 | 支付处理中断 |
| P1高 | 主要功能中断,无替代方案 | 24小时内修复 | 用户无法登录 |
| P2中 | 功能中断有替代方案 | 本次冲刺修复 | 搜索有时返回错误结果 |
| P3低 | 次要问题,化妆品 | 方便时修复 | 按钮对齐偏移2px |
缺陷分类流程(每周)
1. 审查所有新缺陷(未分配)
2. 对于每个缺陷:
a. 再现 — 你能触发它吗?
b. 分类严重性(P0-P3)
c. 估计修复工作量(S/M/L)
d. 分配给所有者+冲刺
e. 链接到相关缺陷/故事
3. 审查上周的P0/P1缺陷 — 它们是否已修复?
4. 关闭无法再现的缺陷(尝试2次后)
5. 更新指标仪表板
缺陷指标仪表板
每周跟踪:
| 指标 | 公式 | 目标 |
|---|---|---|
| 缺陷逃逸率 | 生产中发现的缺陷/总缺陷 | < 10% |
| 平均修复时间(P0) | 从报告到部署的平均小时数 | < 8小时 |
| 平均修复时间(P1) | 从报告到部署的平均小时数 | < 48小时 |
| 缺陷重新打开率 | 重新打开的缺陷/关闭的缺陷 | < 5% |
| 测试逃逸分析 | 应该被抓住的缺陷 | 跟踪 & 减少 |
| 打开缺陷计数 | 按严重性计算的总数 | 下降趋势 |
第8阶段:发布准备
发布清单
在生产发货前:
代码质量:
- [ ] 所有单元测试通过
- [ ] 所有集成测试通过
- [ ] E2E烟雾套件通过
- [ ] 没有新的lint警告/错误
- [ ] 代码审查和批准
- [ ] 这个版本没有已知的P0/P1缺陷
覆盖率和质量门:
- [ ] 行覆盖率 ≥ 目标(80%)
- [ ] 分支覆盖率 ≥ 目标(70%)
- [ ] 与上一个版本相比没有覆盖率下降
- [ ] 突变测试得分 ≥ 60%(如果适用)
性能:
- [ ] 负载测试通过(在阈值内)
- [ ] 与基线相比没有性能回归
- [ ] 捆绑包大小在预算内
安全:
- [ ] 依赖项审计清洁(没有关键/高CVE)
- [ ] 安全清单完成
- [ ] 如有需要,旋转秘密
运营准备:
- [ ] 为新功能配置监控/警报
- [ ] 记录回滚计划
- [ ] 为风险变更设置功能标志
- [ ] 数据库迁移测试和可逆
- [ ] 更新运行手册
发布准备得分
在5个维度上得分0-100:
| 维度 | 权重 | 评分 |
|---|---|---|
| 测试覆盖率 | 25% | 目标达成100,每差一个区域减10分 |
| 缺陷状态 | 25% | 如果0 P0/P1,100,每开放P0减20分,每P1减10分 |
| 性能 | 20% | 如果全部绿色100,每黄色减15分,每红色减30分 |
| 安全 | 20% | 如果清洁100,每关键减25分,每高减15分 |
| 运营 | 10% | 如果清单完成100,每缺少一项减20分 |
发货阈值:≥ 80总体,任何维度不低于60
第9阶段:CI/CD质量门
管道质量门
在CI管道中配置这些门:
# 质量门配置
gates:
- name: "Lint"
stage: pre-commit
command: "npm run lint"
blocking: true
- name: "Unit Tests"
stage: commit
command: "npm test -- --coverage"
blocking: true
thresholds:
pass_rate: 100%
coverage_line: 80%
coverage_branch: 70%
- name: "Integration Tests"
stage: merge
command: "npm run test:integration"
blocking: true
thresholds:
pass_rate: 100%
- name: "Security Scan"
stage: merge
command: "npm audit --audit-level=high"
blocking: true
- name: "E2E Smoke"
stage: staging
command: "npm run test:e2e:smoke"
blocking: true
thresholds:
pass_rate: 100%
- name: "Performance"
stage: staging
command: "npm run test:perf"
blocking: false # 仅警告
thresholds:
p95_regression: 20%
测试自动化成熟度模型
团队评分1-5:
| 级别 | 描述 | 特点 |
|---|---|---|
| 1 — 手动 | 所有测试都是手动的 | 没有自动化,长发布周期 |
| 2 — 反应性 | 一些单元测试,没有CI | 测试在错误后编写,而不是之前 |
| 3 — 结构化 | 测试金字塔,CI管道 | 单元+集成,推送时自动化 |
| 4 — 积极 | 全自动,质量门 | E2E +性能+安全在管道中,TDD |
| 5 — 优化 | 自我修复,预测性 | 易变自动隔离,AI辅助测试,持续部署 |
第10阶段:测试维护
每周测试健康审查
review_date: "[YYYY-MM-DD]"
metrics:
total_tests: 0
pass_rate_7d: "0%"
flaky_tests: 0
flaky_rate: "0%"
avg_suite_duration: "0s"
coverage_line: "0%"
coverage_branch: "0%"
actions:
quarantined: [] # 移动到易变套件的测试
deleted: [] # 删除的测试(过时/无法修复)
fixed: [] # 本周修复的易变测试
added: [] # 新增的测试
trends:
coverage_delta: "+0%" # 与上周相比
flaky_delta: "+0" # 与上周相比
duration_delta: "+0s" # 与上周相比
notes: ""
测试维护规则
- 没有注释掉的测试 — 删除或修复,永远不要注释
- 没有超过2周的跳过测试 — 修复或移除
- 没有测试重复 — 每个行为在正确的级别测试一次
- 测试名称必须可读 — 新人应该明白什么坏了
- 共享测试工具 — 常见设置在fixtures/factories中,不复制粘贴
- 测试数据隔离 — 每个测试创建自己的数据,清理后
- 没有魔术数字 — 在断言中使用命名常量
- 断言消息 — 在复杂断言上自定义消息
常见测试反模式
| 反模式 | 问题 | 修复 |
|---|---|---|
| 休眠测试 | sleep(2000)而不是等待 |
使用显式等待/轮询 |
| 测试相互依赖 | 测试B依赖于测试A的状态 | 隔离 — 每个测试设置自己的状态 |
| 无断言测试 | 测试运行代码但没有断言 | 添加有意义的断言 |
| 脆弱选择器 | 在重新设计时会中断的CSS选择器 | 使用data-testid或aria角色 |
| 上帝测试 | 一个测试验证20件事 | 分割为专注的测试 |
| 模拟过载 | 一切都被模拟,没有真正的测试 | 只模拟外部边界 |
| 硬编码数据 | 当种子数据变化时测试中断 | 使用factories/builders |
| 忽略测试输出 | “它通过了,发货” | 审查为什么它通过了 — 断言有意义吗? |
快速参考:自然语言命令
告诉代理:
- “为[功能]创建测试策略” → 生成策略简报
- “为[函数/文件]编写单元测试” → AAA结构化的测试,包括边缘情况
- “为[模块]审查测试覆盖率” → 差距分析+建议
- “为[API端点]编写集成测试” → 完整的HTTP测试套件
- “为[用户旅程]计划E2E测试” → E2E测试模板
- “为[功能]运行安全清单” → 基于OWASP的安全审查
- “分类这些缺陷:[列表]” → 严重性分类+分配
- “发布准备检查” → 完整的准备得分+阻塞
- “为[端点]性能测试计划” → 负载/压力测试配置
- “修复易变测试[名称]” → 根本原因分析+修复策略