返回自动化工程模块一次订单服务变更的持续反馈
PR 从变化到结论
不稳定用例治理闭环
Automation / Tutorial 15
持续测试与 CI/CD 工程教程
让每次代码变化都尽早获得可信反馈,让失败能归因、报告能追溯、风险能阻断。
10 个章节商城发布流水线GitHub Actions
01
把测试放进交付反馈环
越早反馈越便宜提交本地快速检查
PR冒烟与契约
合并核心回归
发布门禁与灰度
线上指标回流
持续测试不是持续跑全量
优惠金额函数改动后,先在分钟级验证单元与接口规则;进入候选版再验证订单、库存、支付、退款的完整链路。目标是用最小成本尽快暴露当前变更最可能引入的风险。
接口 HTTP 200 只表示请求被正常处理,流水线中的断言仍必须检查响应体业务码、订单状态、金额与副作用。
02
按风险、速度和触发时机分层
冒烟 回归 全量一套测试,不同反馈层
| 触发 | 测试范围 | 目标时长 | 商城示例 |
|---|---|---|---|
| 提交前 | 单元、静态检查 | 秒~3 分钟 | 金额、优惠、状态机 |
| PR | 冒烟 + 契约 + 核心接口 | 5~15 分钟 | 下单、锁库存、支付 |
| 合并后 | 主路径回归 | 15~40 分钟 | 优惠、取消、退款 |
| 夜间/候选版 | 全量 + 多环境专项 | 按项目规模 | 兼容、性能、长尾组合 |
pytest 与 Playwright 标签
# pytest.ini
[pytest]
markers =
smoke: 核心交易冒烟
regression: 主要业务回归
# 运行
python -m pytest -m smoke
python -m pytest -m "regression and not slow"
npx playwright test --grep @smoke冒烟不是随机挑几条快用例。它必须证明版本可继续测试:商城可登录、商品可查、订单可创建、支付依赖可达,且关键响应体结果正确。
03
用 GitHub Actions 建立可执行流水线
配置可复现.github/workflows/quality.yml
name: quality
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- run: pip install -r requirements-test.txt
- name: Run smoke and regression
env:
BASE_URL: ${{ secrets.TEST_BASE_URL }}
TEST_TOKEN: ${{ secrets.TEST_TOKEN }}
run: python -m pytest -m "smoke or regression" --junitxml=reports/junit.xml
- name: Archive test evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: test-report-${{ github.run_id }}
path: reports/
retention-days: 14配置原则
- 锁定运行时版本与依赖,避免本地和 CI 漂移。
- 令牌只从仓库 Secret 注入,不写入代码和日志。
- 设置超时,避免挂起任务长期占用资源。
- 报告上传使用 if: always(),失败时也保留证据。
04
让 PR 检查跟随变更风险
合并前阻断识别路径订单或优惠
选择检查静态到E2E
并行执行缩短等待
汇总证据状态与报告
允许或阻断规则一致
按路径触发交易测试
on:
pull_request:
paths:
- "services/order/**"
- "services/inventory/**"
- "services/payment/**"
- "tests/**"
- ".github/workflows/quality.yml"Branch protection 应要求
- 必需检查名称固定且不能被跳过。
- 新提交会使旧审批和旧结果失效。
- 高风险交易改动至少由代码负责人复核。
- 管理员绕过也应记录原因与审批人。
05
让环境和测试数据可重复
隔离并可清理并行测试唯一标识
import os
from uuid import uuid4
RUN_ID = os.getenv("GITHUB_RUN_ID", "local")
def unique_key(prefix: str) -> str:
return f"{prefix}-{RUN_ID}-{uuid4().hex[:8]}"
# 订单备注、优惠码和测试账号都带运行标识
coupon_code = unique_key("CI-COUPON")准备
- 通过受控测试接口创建商品、库存、优惠和账号。
- 运行记录版本、配置摘要与依赖健康状态。
- 并行任务使用独立租户或唯一标识。
- 资金类动作只进入隔离沙箱。
清理
- 按本次运行 ID 精确清理。
- 清理失败写入报告,不做全库删除。
- 需要审计的订单保留并标记过期。
- 环境漂移时立即中止并标记为环境问题。
06
先归因,再决定重跑或阻断
失败不是一个红点失败归因决策表
| 类别 | 证据 | 行动 |
|---|---|---|
| 产品缺陷 | 相同版本和数据可重复,结果违反业务规则 | 阻断并关联缺陷 |
| 测试缺陷 | 定位、等待、断言或清理错误 | 修复脚本后重跑受影响范围 |
| 环境问题 | 服务不可用、配置漂移、容量不足 | 恢复环境并保留运行证据 |
| 数据问题 | 库存被占用、优惠过期、账号污染 | 修复数据工厂与隔离 |
| 不稳定测试 | 相同提交多次运行结果不一致 | 隔离、统计、限期治理 |
为每次调用保留关联信息
response = order_client.create_order(payload)
logger.info(
"create_order finished",
extra={
"run_id": run_id,
"commit": commit_sha,
"order_id": response.json().get("data", {}).get("orderId"),
"business_code": response.json().get("businessCode"),
},
)
assert response.status_code == 200
assert response.json()["businessCode"] == "SUCCESS"重跑通过不等于首次失败消失。先保存原始日志、请求关联 ID、截图或 trace,再重跑确认是否不稳定。
07
把 Flaky Test 当作工程缺陷治理
统计 修复 退出识别同提交结果漂移
隔离不掩盖主结果
收集时间与证据
修复等待数据环境
退出隔离连续稳定
治理规则
- 用相同提交、环境和数据重复确认,不凭一次重跑下结论。
- 为用例登记负责人、首次出现时间、失败率和业务风险。
- 高风险冒烟用例即使 Flaky 也不能静默放行,应优先修复或替换。
- 临时隔离必须设置期限;隔离套件仍定时运行并通知负责人。
- 只有找到原因且达到约定的连续稳定次数后,才退出隔离。
有限重试并保留首次失败
# pytest.ini
[pytest]
addopts = --reruns 1 --reruns-delay 1
# Playwright
retries: process.env.CI ? 1 : 0,
trace: 'retain-on-failure'08
归档报告,并把通知发给能行动的人
证据可追溯报告至少包含
- 仓库、提交 SHA、分支、工作流与运行链接。
- 环境、版本、数据批次和触发人。
- 通过、失败、跳过、重试与 Flaky 数量。
- 订单号、请求关联 ID、失败步骤和关键响应体。
- JUnit、HTML、截图、Trace 和服务日志。
失败后通知示意
- name: Notify failure
if: failure()
env:
WEBHOOK_URL: ${{ secrets.QA_WEBHOOK_URL }}
run: |
python scripts/notify.py --status failed --run-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" --report "test-report-${{ github.run_id }}"通知应包含“谁需要做什么”和运行链接,不能只发送“测试失败”。Webhook、Token 和用户数据不得出现在报告正文。
09
把流水线结果转换为发布门禁
证据驱动放行商城交付的关键门禁
| 阶段 | 必需证据 | 阻断条件 |
|---|---|---|
| PR 合并 | lint、类型、单元、冒烟 | 任何必需检查失败 |
| 测试环境部署 | 迁移检查、健康检查 | 版本或依赖不一致 |
| 候选版 | P0/P1 回归、已知风险评审 | 交易核心失败或证据缺失 |
| 生产发布 | 审批、灰度、监控、回滚 | 不可观测或不可回退 |
环境保护与人工审批
jobs:
deploy-production:
needs: [test]
if: github.ref == 'refs/heads/main'
environment: production
runs-on: ubuntu-latest
steps:
- run: ./scripts/deploy.sh
- run: ./scripts/verify-smoke.sh生产 environment 应在仓库设置中配置审批人和保护规则。配置文件表达依赖关系,审批权限不能靠脚本里的注释代替。
10
完成一条可诊断的持续测试流水线
练习与验收练习:为商城交易服务建立 CI/CD
- 把单元、冒烟、回归和全量测试按触发时机分层。
- 创建 GitHub Actions 工作流,锁定 Python 版本并缓存依赖。
- 在 PR 中并行执行静态检查、单元测试和交易冒烟。
- 使用 Secret 注入测试地址和令牌。
- 故意制造产品、脚本、环境、数据四类失败并完成归因。
- 为一条偶发等待用例建立 Flaky 登记、隔离和退出标准。
- 无论成功失败都归档 JUnit、HTML 与 trace,设置保留期限。
- 发送包含负责人行动和运行链接的失败通知。
- 配置候选版与生产门禁,演练一次阻断和一次回滚。
反馈够快
- 分层有依据
- PR 时长受控
- 任务可并行
- 缓存不污染
失败可信
- 原始证据保留
- 归因规则一致
- 重试次数有限
- Flaky 有负责人
发布可控
- 门禁不可静默跳过
- 报告可追溯
- 通知可行动
- 灰度回滚就绪
流水线已经能持续验证单个应用。下一步进入服务依赖、故障传播和分布式事务。
继续学习微服务测试