写在前面 :这是一份面向 测试开发(QA/自动化测试)岗位 的 CI/CD 面试题精编。不考运维部署、不问底层架构,聚焦"你能不能独立把自动化测试接进流水线、出了问题能不能定位、流程能不能设计好"。
Q1. 你平时是怎么把自动化测试接进 CI/CD 流水线的?
难度 :⭐ ⬜ ⬜ ⬜ ⬜ | 定位:入门题,验证你到底有没有真实落地经验
得分点
- 知道在项目根目录写
.gitlab-ci.yml/Jenkinsfile/ GitHub Actions 的 workflow yaml` - 能说出 stage → job → script 的基本结构
- 知道把测试结果以报告形式回传,而不是只靠看日志
标准答法
以 GitLab CI 为例,在仓库根目录创建 .gitlab-ci.yml:
stages:
- test
unit-test:
stage: test
image: python:3.11
script:
- pip install -r requirements.txt
- pytest tests/ --junitxml=report.xml
artifacts:
reports:
junit: report.xml # 关键:让测试结果在 MR 页面可视化
核心就三步:① 定义阶段 ② 写 Job 跑测试命令 ③ 用 artifacts 把测试报告挂出来。
进阶答法(眼前一亮版)
- 把 JUnit 报告 用
artifacts.reports.junit回传,MR 页面直接展示通过率/失败用例,评审人不用点进 Job 看日志; - 把 HTML 报告 (Allure / pytest-html)部署到 GitLab Pages 或对象存储,失败时贴链接;
- 截图、录屏用
artifacts: when: on_failure只在失败时保留现场,省磁盘; - 公共配置抽成
include模板,各业务仓库一行引入,避免每个仓库抄一遍。
避坑提示
只说"写个 yml 跑 pytest"是及格线。主动提到 junit 报告回传 + 失败产物保留,面试官立刻知道你真的在生产环境干过。
Q2. 你们的测试是每次 push 都全量跑,还是按条件触发?怎么控制的?
难度 :⭐⭐ ⬜ ⬜ ⬜ | 定位:考察你对"流水线效率"的意识
得分点
- 知道用
rules(GitLab)/when(GitHub Actions) 控制触发条件 - 理解不同测试类型应跑在不同时机(单元测试随时跑、E2E/性能测试谨慎跑)
标准答法
按测试"重量级"分层触发,避免每次 commit 都跑全量:
# 轻量单测:push 到任何分支都跑
unit-test:
stage: test
script: [npm run test:unit]
rules:
- if: $CI_PIPELINE_SOURCE == "push"
# E2E:只在 MR 时跑
e2e:
stage: test
script: [npm run test:e2e]
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# 性能测试:只在打 tag 时跑
perf-test:
stage: test
script: [k6 run perf.js]
rules:
- if: $CI_COMMIT_TAG =~ /^release-.*/
进阶答法
- 按变更目录触发 :
rules: changes------ 只改了api/才跑 API 测试,前端改动不跑后端用例; - MR 合并前必跑 :配合
workflow:rules避免 push 和 MR 双触发导致重复跑; - 手动卡点 :生产环境的回归用
when: manual,需要人点一下才跑,防止误操作。
避坑提示
如果说"全部测试每次 push 都跑",面试官会认为你不关心流水线耗时。分层触发是测试开发的基本素养。
Q3. 测试 Job 失败了,你的排查思路是什么?
难度 :⭐⭐ ⭐ ⬜ ⬜ | 定位:考察排障方法论,区分"只会看日志"和"有系统思路"
得分点
- 能区分 测试代码报错 / 环境问题 / 偶现失败(flaky) 三类
- 有一套从日志到现场的固定流程
标准答法(按步骤说,面试官最爱这种条理性)
- 看 Job 日志:先定位是测试断言失败,还是报错栈(import 失败、连接超时等);
- 判断环境 vs 代码 :
- 报错
ModuleNotFoundError/Connection refused→ 环境(依赖、服务); - 报错
AssertionError→ 测试代码或业务逻辑;
- 报错
- 本地复现 :用 Job 里相同的
image起容器,跑同一条命令; - 偶现失败(flaky)排查 :
- 并发测试抢端口 / 抢数据库数据 → 用独立端口、每用例隔离数据;
- 服务没完全启动就跑测试 → 加
wait-for-it/ 健康检查轮询;
- 看 artifacts:下载测试报告、截图、录屏分析;
- 怀疑 Runner 资源:对比 Job 时长,平时 2 分钟这次 10 分钟 → 可能是资源争抢。
进阶答法
- 把 失败截图/日志/录屏 自动上传到 Allure / 测试报告平台,关联用例 ID 追踪历史稳定性;
- 用 retry(失败自动重试 1~2 次) 兜偶现失败,但要配合统计:如果某个用例 retry 率持续 >5%,说明是真 bug 不是 flaky,要单独跟进;
- 分层超时 :
timeout设合理值,避免一个卡住的用例拖住整条流水线。
避坑提示
回答"我就看日志"= 及格。主动说出"环境 vs 代码分类 + flaky 治理 + 失败产物留存"= 优秀。
Q4. UI 自动化(Selenium / Playwright)怎么在 CI 里跑?
难度 :⭐⭐ ⭐ ⬜ ⬜ | 定位:高频实战题,几乎必问
得分点
- 知道 CI 环境是 Linux + 无头(headless),本地有界面 ≠ CI 能跑
- 知道用 自带浏览器依赖的镜像,而不是裸镜像自己装 Chrome
标准答法(Playwright 为例)
e2e:
stage: test
image: mcr.microsoft.com/playwright:v1.40.0 # 关键:官方镜像已带所有浏览器依赖
script:
- npm ci
- npx playwright test
artifacts:
when: always
paths: [playwright-report/]
expire_in: 7 days
进阶答法
- 失败自动录屏 + trace :Playwright 的
trace.zip能完整回放每一步,CI 里必开; - 并行分片 :
playwright test --shard=1/4配合parallel: 4把一个长 E2E 拆成 4 份并发,缩短墙钟时间; - 和后端联调 :用
services起一个测试用后端容器,测试数据库每次重建,保证隔离; - 无头模式 + 固定 viewport:避免容器里因为没有显示器报错。
避坑提示(面试官专挑这里追问)
| 坑 | 原因 | 解决 |
|---|---|---|
chromedriver 版本不匹配 |
本地 Chrome 自动升级 | 用官方 playwright 镜像 |
报错 session not created / no display |
没开 headless | CI 强制 headless: true |
| 测试偶发超时 | 页面加载慢 / 网络抖动 | 加大 timeout,加重试 |
Q5. 测试需要依赖数据库、Redis、第三方服务,CI 里怎么处理?
难度 :⭐⭐ ⭐ ⬜ ⬜ | 定位:考察"测试隔离"意识
得分点
- 知道用
services(GitLab)/containers(GitHub Actions) 起配套容器 - 理解每个 Job 拿到干净实例、跑完即毁 = 测试互不污染
标准答法
test:
stage: test
image: python:3.11
services:
- postgres:15
- redis:7
variables:
POSTGRES_HOST: postgres # service 名即 hostname
POSTGRES_PORT: "5432"
REDIS_HOST: redis
script:
- pytest tests/ --db-host=$POSTGRES_HOST
进阶答法
- 测试数据隔离:每个用例用事务回滚 / fixture 重建表,避免用例 A 的数据影响用例 B;
- 复杂依赖用 docker-compose :把整个依赖栈(
docker-compose.test.yml)在before_script里up,测试完down; - 第三方服务打桩 :对外部支付/短信等不可控服务,用 mock server(如 WireMock / mockserver) 替代,保证 CI 稳定可重复;
- 不要依赖"共享测试环境"------那是数据冲突和"在我机器上能跑"的根源。
避坑提示
如果说"我们有一个专门的测试环境,大家连同一个数据库",面试官会扣分。CI 里每个 Job 独立、可重建才是正确思路。
Q6. 测试产物(报告、截图、录屏)怎么管理和查看?
难度 :⭐⭐ ⭐ ⬜ ⬜ | 定位:考察"测试可观测性"
得分点
- 知道 JUnit 报告能在 MR 直接看结果
- 知道 artifacts 的 when / expire_in 控制留存策略
标准答法
test:
script: [pytest --junitxml=report.xml]
artifacts:
reports:
junit: report.xml # → MR 页面展示通过/失败数
when: always # 无论成功失败都保留
paths: [screenshots/, logs/]
expire_in: 7 days # 自动清理,省磁盘
进阶答法
- 分层留存 :失败产物长期保留(
expire_in: 30 days),成功产物短期清理; - Allure Report :
allure generate后挂到 GitLab Pages / S3,全团队可访问历史趋势; - 报告聚合 :多 Job 并行的测试报告用
artifacts:reports:junit自动合并,一条流水线一份总报告; - 失败告警:测试失败 → Webhook → 飞书/企微群通知责任人。
避坑提示
expire_in不设 = 产物永久堆积撑爆磁盘。生产级流水线一定配过期策略。
Q7. 怎么实现"测试不通过就不能合并 MR"?
难度 :⭐⭐ ⭐⭐ ⬜ | 定位:考察"质量门禁"概念
得分点
- 知道 GitLab 的 "Pipelines must succeed" 设置
- 知道 Approval Rules 做人工卡点
标准答法
Project Settings → Merge Requests:
- ✅ Pipelines must succeed(流水线不绿不能合)
- ✅ All threads must be resolved(讨论要闭环)
- 可选:Approval rules(至少 N 人审批)
这样 MR 页面会阻塞合并,直到 Pipeline 全绿。
进阶答法
- 分支保护(Protected Branch) :
main/release分支禁止直接 push,只能通过 MR + 流水线通过合并; - MR 级 Pipeline :
rules: if: $CI_PIPELINE_SOURCE == "merge_request_event",确保合入前的代码是被测过的(而不是 push 时的旧 commit); - E2E / 安全扫描设为 required :关键质量关卡 Job 失败直接阻断,可选 Job(如文档构建)设
allow_failure: true不阻断; - 覆盖率卡点 :
coverage: '/Total.*?([0-9]{1,3})%/'+ 设置最低覆盖率阈值,低于阈值不让合。
避坑提示
只说"配一下设置"太浅。主动提到 Protected Branch + MR Pipeline + 覆盖率阈值,面试官会觉得你有工程化思维。
Q8. 你遇到过"本地能跑、CI 跑不通"的情况吗?怎么解决的?
难度 :⭐⭐ ⭐⭐ ⬜ | 定位:行为面试题,验证真实经验 + 排查能力
标准答法(准备 2~3 个真实场景,面试官最爱)
① 文件路径大小写
- 现象:本地 macOS 不区分大小写,
import User能找到user.py;CI 用 Linux 严格区分 → 报ModuleNotFoundError; - 解决:统一小写命名,CI 加
ls调试确认。
② 硬编码 localhost
- 现象:本地连
localhost:3306,CI 里服务在另一个容器 →Connection refused; - 解决:用
services的 service 名当 hostname(如mysql),配置从环境变量读。
③ 依赖版本漂移
- 现象:本地
npm install装了最新版,CI 用锁文件装旧版 → 行为不一致; - 解决:CI 统一用
npm ci/pip install --requirement锁版本。
④ 并行测试端口冲突
- 现象:本地单进程跑通,CI
parallel跑多 worker 抢同一个端口; - 解决:每个 worker 动态分配端口(如
0让系统分配)。
进阶答法
- 环境一致性 :用 Dockerfile 固化测试环境,本地和 CI 跑同一个镜像,从根源消除"环境差异";
- CI 本地复现工具 :GitLab 的
gitlab-runner exec docker/ GitHub Actions 的act,在本地就能模拟 CI 环境调试; - 失败快照:把 CI 的失败日志、截图、环境变量完整留存,对比本地。
避坑提示
这题没真实踩过坑基本编不出来。建议提前准备 2 个自己的真实案例(哪怕是练习项目),讲清楚"现象 → 排查 → 根因 → 解决"。
Q9. 大仓库 / Monorepo 怎么优化"只跑改动的测试"而不是全量跑?
难度 :⭐⭐ ⭐⭐ ⭐ | 定位:进阶题,区分普通 QA 和测试架构思维
得分点
- 知道
rules: changes按目录触发 - 知道基于 git diff 的智能测试选择(Test Impact Analysis)
标准答法
① 按目录粗粒度分流
api-test:
rules:
- changes: [api/**, tests/api/**]
web-test:
rules:
- changes: [web/**, tests/web/**]
只改了 api/ → 只跑 API 测试。
② 基于 diff 的智能选择
- Python:
pytest-testmon(根据源码变更自动选受影响用例); - JS:
jest --changedSince=main; - 原理:建立"源码文件 ↔ 测试用例"的依赖图,只跑受本次改动影响的用例。
进阶答法
- 父子流水线(Parent-Child Pipelines):父 Job 分析变更范围 → 动态生成子流水线配置 → 只触发受影响模块的测试和部署;
- 测试分层金字塔 :单测极快(秒级,每次 push 必跑)、集成测试(分钟级,MR 跑)、E2E(十级分钟,仅关键路径 + manual),越往上越贵、越少跑;
- 缓存依赖 :
cache: key: { files: [package-lock.json] }让锁文件不变就不重装依赖。
避坑提示
如果面试的是中高级岗位,这题必问。提前准备"你们项目是怎么分层的"这套说辞,命中率极高。
Q10.(压轴)如何设计一条"从代码提交到测试报告"的完整 CI/CD 流水线?
难度 :⭐⭐ ⭐⭐ ⭐ | 定位:综合设计题,考察全局观
标准答法(给一个端到端模板 + 讲解设计思路)
stages:
- lint # 代码规范(最快,最先拦)
- build # 构建 + 单测
- test # 集成 / E2E
- report # 汇总报告
- deploy # 部署测试环境(可选 manual)
# 1. 代码规范 ------ 最快反馈
lint:
stage: lint
script: [npm run lint]
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# 2. 构建 + 单测 ------ 产出构建物
build:
stage: build
script: [npm ci, npm run build, npm run test:unit]
artifacts:
paths: [dist/]
reports:
junit: junit-unit.xml
# 3. 集成测试 ------ 起依赖服务
integration-test:
stage: test
needs: [build]
services: [postgres:15, redis:7]
script: [npm run test:integration]
artifacts:
reports:
junit: junit-int.xml
# 4. E2E ------ 用 needs 跳过 lint,不等它
e2e:
stage: test
needs: [build]
image: mcr.microsoft.com/playwright:v1.40.0
script: [npx playwright test]
artifacts:
when: on_failure
paths: [playwright-report/]
# 5. 报告汇总 ------ 永远跑,方便看总览
report:
stage: report
needs: [integration-test, e2e]
when: always
script: [./merge-reports.sh]
artifacts:
paths: [allure-report/]
expire_in: 7 days
# 6. 部署测试环境 ------ 手动卡点
deploy-staging:
stage: deploy
needs: [report]
when: manual
environment: staging
script: [./deploy.sh staging]
设计思路讲解(面试时口述这 5 点,直接加分)
- 分层 stage,越靠后越重:lint(秒)→ build(分钟)→ test(十级分钟)→ deploy(按需 manual);
- 快反馈优先:把最快失败的 lint 放最前,先拦低级错误省资源;
- 用
needs做 DAG :E2E 只等 build,不等 lint,缩短墙钟时间; - 测试隔离 :依赖用
services起,每个 Job 干净环境; - 产物策略:失败才留现场、报告统一汇总、设过期清理。
进阶答法(高级岗加分项)
- 质量门禁:Protected Branch + MR Pipeline + 覆盖率阈值 + 必要审批;
- 动态环境:MR 开 → 自动部署一个隔离的 Review App,MR 关 → 自动销毁;
- 可观测性:JUnit 报告 + Allure 趋势 + 失败告警到 IM;
- 安全左移 (如有):在
lint后加 secret detection / SAST(GitLab EE 或用 trivy/gitleaks 平替)。
避坑提示
这题不要求你背完整 YAML,面试官看的是你有没有"流水线设计"的意识 :分层、快反馈、隔离、门禁、可观测。把这 5 个词记住,比背 100 行配置有用。