CI 中的测试
这一课你将能做到
- 读懂并标注本站
.github/workflows/ci.yml 里 backend / platform 两个 job 的测试命令与顺序
- 说清
mvn test 与 mvn verify 的差异,并让本地习惯与 CI 严格对齐
- 解释并行 job、
concurrency 取消旧 run、失败阻断合并的工程含义,并画出走测试分轨的草图
为什么要学
个人笔记本上的绿测挡不住两件事:「我忘了跑」和「我机器有特殊配置」。持续集成(CI) 在每次 push / PR 时用干净环境自动跑测试——失败即红,阻止未验证代码进入主线。人话:CI 是团队共享的红绿灯,它比同事更不讲面子,也比你本地环境更干净。
CodeForge 的 ci.yml 是现成范本:backend 跑 mvn -B verify,platform 跑 npm test + npm run build。学完本课,你应能在 PR 页 30 秒内定位红灯来源,并知道「为什么我的测试要这么挂」。写测试的手法在前 10 课;本课讲测试如何成为工程纪律。
没有 CI 时,测试停留在「有纪律的人偶尔跑」;有 CI 后,测试成为默认路径。金字塔策略也在这里落地:CI 里快测必跑,慢测按频率分轨,避免红绿灯变成永远排队的黄灯。
先建立图像
push / PR
│
├─► job backend: setup-java → mvn -B verify
└─► job platform: npm ci → npm test → npm run build
▲
两者并行;总时长 ≈ max(两者),不是相加
concurrency:同一分支新 push 取消旧 run(省分钟)
| 命令 | 含义 |
|---|
mvn test | 跑 Surefire 快测(*Test) |
mvn verify | test + 打包 / JaCoCo report / Failsafe IT 等后续阶段 |
npm test | 前端单元 / 组件测 |
npm run build | 编译打包,放测试后以免白跑 |
任一 job 失败,PR 不应合并。Checks 是「合并前的最后读者」。
跟着做
步骤 1:打开并标注 ci.yml
一份与 CodeForge 结构一致的带注释样例(细节以仓库真实文件为准):
name: ci
on:
push:
branches: [main]
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # 连续 push 只跑最新
jobs:
backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17" # 与生产一致
cache: maven # 依赖缓存,省下载分钟
- name: 测试与打包
run: mvn -B verify
working-directory: backend
- name: 上传测试报告
if: always() # 红了也要能看报告
uses: actions/upload-artifact@v4
with:
name: surefire-reports
path: backend/target/surefire-reports/
platform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
cache-dependency-path: platform/package-lock.json
- run: npm ci # 锁文件为准,比 npm install 可复现
working-directory: platform
- run: npm test
working-directory: platform
- run: npm run build # 测试绿了才值得打包
working-directory: platform
在笔记里标出:两个 job 的 working-directory、JDK/Node 版本、缓存配置、实际测试命令。
步骤 2:本地对齐 backend
cd backend
mvn test # 日常快反馈
mvn -B verify # 提交前对齐 CI(含 JaCoCo report / IT)
记下两者耗时差。-B 是 batch 模式,日志干净适合机器读。若 verify 比你预期的慢,看是哪个插件阶段(打包 / IT / 报告)——这是第 6、10 课分轨知识的用武之地。
步骤 3:本地对齐 platform
cd platform
npm ci
npm test
npm run build
顺序与 workflow 一致。npm ci 严格按 package-lock.json 安装——「我这能过」与 CI 的差异多半藏在依赖版本里。
步骤 4:理解并行与 concurrency
时间轴 ─────────────────────────────────►
backend ████████████
platform ████████████████
总等待 ≈ 较长的那条,而不是相加
cancel-in-progress 的含义:同一 PR 连续 push 三个 commit,前两个 run 被取消,只跑最新的。人话:最新提交说了算,旧结果没有意义,排队分钟全是浪费。
步骤 5:读一次红灯的正确姿势
- 点开失败的 job(backend 还是 platform?失败归属先分家)
- 展开失败的 step(
mvn -B verify 还是 npm test?)
- 搜索第一个
FAILURE / AssertionError / FAIL
- 记下:类名、方法名、期望 vs 实际
- 本地复现:
mvn test -Dtest=那个类#那个方法
CI 日志很长,但导航仪只在「第一处失败」。修那一处再重跑,比从末尾汇总倒推快得多。有意在自己分支制造一次红灯并走完这个流程(别合进 main),比读十遍文章记得牢。
步骤 6:为慢测画分轨草图
| 频率 | 放什么 | 例子 |
|---|
| 每个 PR | 快测 | *ServiceTest、Vitest、lint |
| 主分支 / 每日 | 中速 | Testcontainers IT(*IT)、全上下文测 |
| 发布前 | 慢测 | Playwright 冒烟 3~5 条 |
今天的 ci.yml 可能还没拆这么细——草图的价值是:你以后每加一种测试,都知道该挂哪个档位,而不是一股脑塞进 PR job 拖垮所有人。
讲清楚要点
- 环境必须干净可复现:CI 固定 JDK/Node 版本 + 锁文件 + 依赖缓存——「我这能过」往往就差在版本号上。
- 分钟数是钱也是反馈:job 并行、失败早停、concurrency 取消旧 run——流水线体验本身就是产品。
- 本地与 CI 命令一致是纪律:提交前跑同一条
mvn -B verify / npm test,消灭幽灵红灯。
- 红灯必须修到绿才能合并:跳过检查需要强理由与权限约束,否则主线质量只靠自觉。
- 双端拆 job 而不是一个大 job:backend 红、platform 绿,一眼看清失败归属。
- 不是所有测试都挡每次 push:IT / E2E 分频运行,快测守门、慢测巡逻。
你可能会懵的地方
-
mvn test 绿、verify 红?
verify 还跑了打包、JaCoCo check、Failsafe IT 等阶段。读失败的是哪个插件,不要只盯 Surefire 的测试结果。
-
CI 偶发怪红是缓存坏了吗?
可能但少见。先重跑一次 workflow;反复红就不是玄学,去修锁文件与版本固定,而不是无限重跑碰运气。
-
fork PR 为什么没有秘密变量?
安全限制,属预期行为。E2E 若需要密钥,用 OIDC 或标签触发的设计,别把密钥塞给任意 PR。
-
concurrency 的 group 为什么带 ref?
github.workflow-ref/ref 组合让不同分支互不取消,只有同一分支的连续 push 互相替代——取消范围精确到「过时的自己」。
-
CI 会帮我发现「没写的测试」吗?
不会。CI 只强制执行已有测试;「该测什么」靠金字塔分层与覆盖率导航(第 10 课),两者配合才是完整闭环。
错法对照
| 错法 | 问题 | 正解 |
|---|
| 本地从不跑测只靠 CI | 反馈慢、烧分钟 | 先本地 mvn test / npm test |
| 本地命令与 workflow 不一致 | 幽灵红 / 假绿 | 对齐 verify 与 scripts |
| 红灯强行 merge | 主线腐烂 | 修到绿,例外走审批 |
| 所有慢测塞进每个 PR | 队列爆炸 | 分轨:PR / 主分支 / 发布前 |
| 只读日志末尾汇总 | 被噪声带偏 | 先读第一个失败堆栈 |
卡点急救
- CI 上
mvn -B verify 找不到 pom:根目录没有聚合 pom 时,必须 working-directory: backend 或 --file backend/pom.xml;本地能跑是因为你 cd 过去了。
- 平台 job 报 node 版本不匹配:workflow 的
node-version 与本地 .nvmrc/ engines 对齐;依赖 npm ci 而非 npm install,避免锁文件漂移。
- 测试报告没上传:
upload-artifact 少了 if: always()——失败时 job 中断,后面的 step 不执行,红跑一场却拿不到现场。
面试常问
-
为什么 CI 里用 mvn verify 而不是只 mvn test?
verify 覆盖 test 之外的生命周期阶段:打包验证、JaCoCo 报告与 check 门禁、Failsafe 跑 *IT 集成测。只跑 test 会让「门禁与慢测」形同虚设——本地 test 图快,提交前与 CI 的 verify 对齐是纪律。
-
PR 流水线太慢,你怎么优化?
四招:job 并行(总时长取 max);依赖缓存(setup-java cache: maven、setup-node cache: npm);concurrency 取消过时 run;慢测分轨(IT/E2E 挪到主分支或 nightly)。本质是把「每个 push 都全量」改成「分级验证」。
-
CI 红灯时的标准处理流程?
定位 job → 定位 step → 读第一个失败堆栈 → 本地用 -Dtest=类#方法 复现 → 修复 → 重跑确认绿。禁止两个动作:跳过检查合并、无限重跑碰运气。
自测三问
- 本站 backend job 为什么用
mvn -B verify 而不是只 test?
- backend 与 platform 并行时总时长怎么估算?串行又会怎样?
cancel-in-progress 对连续 push 的同一 PR 有什么好处?
小结一句话
CI = 团队共享的 mvn verify + npm test;绿测才能安心合并,测试从个人习惯升级为工程纪律。
延伸