ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Jenkins下RobotFramework失败用例自动重执行机制详解

Jenkins下RobotFramework失败用例自动重执行机制详解

做自动化测试的朋友,十有八九都遇到过这种尴尬:半夜 Jenkins 跑完 RobotFramework 用例,早上打开邮件一看,几十条失败,仔细追查却发现大半是环境抖动、网络超时、某个服务还没起来。于是你只能手动重跑一遍,或者自认倒霉修复后再跑一轮。这套流程既浪费时间,又掩盖了真实缺陷的排查优先级。今天的主题很直接——怎么在 Jenkins 里给 RobotFramework 的失败用例加一道“重执行”机制,让偶发失败自动重跑、真失败保留现场,一套方案把效率和可信度一起提上来。

这套方案适合谁?适合已经用 Jenkins 做定时任务、且测试脚本基于 RobotFramework 的团队,也适合个人维护多个测试任务、想降低误报率又不想过度改造现有框架的朋友。核心思路不复杂,但里面的细节坑不少,我尽量把原理、参数、配置步骤和踩坑经验都揉开了讲。

1. 问题复盘:为什么失败用例需要“重执行”

1.1 自动化用例失败的两种典型场景

先理清一个概念:失败不等于缺陷。RobotFramework 用例执行失败,原因可以分成两大类。

第一类是确定性失败,也就是脚本逻辑、被测功能本身的 bug、数据准备错误导致的失败,这类失败无论你跑多少遍都会挂,必须老老实实提单修复。第二类是非确定性失败,也叫 flaky test(不稳定测试),典型症状是“这次跑挂了,下次跑又好了”。触发因素包括服务器响应超时、测试环境网络抖动、浏览器启动慢、前置服务没有就绪、数据库连接池暂时耗尽、并发任务抢占资源等。这类失败最让人头疼,因为它不是真实的缺陷,却在报告里占用你的注意力,还会导致 CI 流水线整体标红,影响发布判断。

1.2 手动重跑的隐性成本

很多团队面对 flaky test 的做法是:人工触发重新构建,或者在本地把失败脚本再跑一遍确认。问题在于,Jenkins 默认构建记录里,上一次失败现场的输出日志往往被新一次构建覆盖或归档混乱,追查起来非常费劲。而且自动化测试的价值之一就是无人值守,一旦需要人工介入重跑,就失去了“自动报告可信结果”的意义。

所以失败重执行方案的核心目标可以归纳为三点:

  • 减少人为介入,让不稳定失败在下一个执行周期内自动被重新验证。
  • 保留失败现场,重跑不是简单“再来一次”,而是要有策略、有记录、有汇总。
  • 最终报告可信,让每次构建的结论接近“真实产品状态”,而不是环境噪声。

1.3 为什么选择 Jenkins + RobotFramework 组合

Jenkins 是目前使用范围最广的持续集成调度器,RobotFramework 则是关键词驱动测试框架的代表,两者配合在 UI 自动化、接口自动化、业务链路自动化场景中都非常常见。RobotFramework 自身提供了--rerunfailed这样的重执行参数,Jenkins 侧则负责把它串成完整流程。换句话说,失败重执行的“智能”主要来自 RobotFramework 的能力,“流程编排”主要靠 Jenkins 的灵活配置。这套方案不依赖额外插件,维护成本低,适合绝大多数已有 Jenkins+RF 基础的团队快速落地。

2. 核心技术选型:三种失败重执行思路的对比

2.1 方案一:基于--rerunfailed的二次执行

RobotFramework 从早期版本开始就内置了一个输出目录管理机制:每次执行会在输出目录写入output.xml。配合--rerunfailed output.xml参数,框架会解析该输出文件,只挑选其中失败或异常的用例重新执行,失败列表会写入rerun.xml,最后再把两轮结果合并成一个完整的output.xml和测试报告。

这是我个人最推荐的方案,因为它是官方机制、无需定制代码、对环境依赖最小。它适合绝大部分重执行场景,尤其是那些失败率不高但偶尔抽风的测试套件。

核心命令行大致是:

robot --outputdir result --output original.xml --log original_log.html --report original_report.html tests/ robot --outputdir result --rerunfailed result/original.xml --output rerun.xml --log rerun_log.html --report rerun_report.html tests/ rebot --outputdir result --output output.xml --log log.html --report report.html --merge original.xml rerun.xml

注意第三行是关键:使用rebot --merge把首轮结果和重跑结果按套件、用例级别合并,最终 html 报告里同一用例展示的是最后一次执行结果,但输出 xml 中会包含历史执行记录,方便溯源。

2.2 方案二:基于--retry参数的即时重试

RobotFramework 6.0 开始新增了--retry参数,允许在单次执行过程中对失败用例进行即时重试,无需二次调度。它的用法是:

robot --retry 2 --retry-dry-run tests/

--retry 2表示失败用例最多再执行 2 次,--retry-dry-run是一个可选的预检步骤,先以 dry-run 模式跑一遍确认没有语法层面错误,再进入真实验证。注意这个方案不区分“失败场景”,只要用例失败就重试,且重试次数内如果成功,最终状态标记为通过,但报告里仍然能看到历史失败痕迹。

两种思路的本质区别:

特性--rerunfailed--retry
适用版本RF 3.x 及以上RF 6.0 及以上
是否需二次调度是否
是否保留首轮失败现场是,通过 rebot merge是,输出中保留 trace
对前置条件的处理重跑时需自行处理依赖同一次执行,环境状态连续
并发能力可配合 pabot单进程执行,效率较低

如果你的 RobotFramework 版本还是 5.x,就别惦记--retry了,老老实实用--rerunfailed。如果是 6.0 以上的新项目,且用例量不大、执行时间可接受,--retry的配置更简单。

2.3 方案三:pabot 并行分片 + 失败合并重跑

当测试集合的体积大到单进程跑不完、或者用例之间存在较弱的耦合关系时,很多团队会引入 pabot 做分片并行执行。此时失败重执行同样适用,只是命令要稍作调整。pabot 本身有--rerunfailed选项,也可以先执行 pabot 生成多个 output 文件,再统一 rebot 合并。

pabot --processes 4 --outputdir result --output original.xml tests/ pabot --processes 4 --outputdir result --rerunfailed result/original.xml --output rerun.xml tests/ rebot --outputdir result --output output.xml --log log.html --report report.html --merge original.xml rerun.xml

这里有个细节:pabot 默认会把每个分片的结果文件放在result/pabot_results目录下,主original.xml里已经包含汇总信息。执行 rerun 时,依然对主original.xml使用--rerunfailed即可。合并报告用 rebot 处理两个主输出文件就行。愿意做得更精细的话,pabot 还支持--rerunfailed与--priority组合使用,让重跑任务优先调度给空闲进程。

2.4 三个方案的适用边界与选择建议

我的建议是:

  • 如果你们用的是 RF 6.0 以下版本,无脑选方案一。
  • 如果用例数量少、执行时间短、希望配置最简,方案二足够。
  • 如果用例量大、已经用了 pabot 并行,方案三是标准配置。
  • 如果想要最高级别可靠性,也可以方案三 + Jenkins 构建后置步骤的组合,让 pabot 首轮执行完、检查失败用例、决定是否需要 rerun 阶段。

无论选哪种,Rich 的失败现场收集能力都建议同步改造:在测试里增加对失败截图、页面源码、接口返回内容的附加到日志环节,这样重跑是否成功,你都有充足的排障信息。

3. Jenkins 侧流水线设计:完整自动化重跑闭环

3.1 前置准备:在机器上准备好 RF 和 pybot 环境

在 Jenkins 中运行 RobotFramework,首先要保证执行节点上安装了 Python、RobotFramework 和必要的库。常见两种形式:

  • 全局环境:直接在节点上pip install robotframework,Jenkins 任务里直接调用robot命令。
  • 虚拟环境:每个项目或每天流水线里创建 venv,避免不同项目依赖冲突。

我建议用虚拟环境,尤其是团队里有多个项目、依赖版本不一致的时候。在 Jenkins Pipeline 中,可以这样写:

stage('Prepare Env') { steps { sh ''' python3 -m venv venv . venv/bin/activate pip install --upgrade pip pip install robotframework pip install -r requirements.txt ''' } }

3.2 时间戳目录:根治输出覆盖问题

失败重执行遇到的最常见问题,是首轮和重跑轮结果写到了同一个目录,导致output.xml被覆盖。我的建议是 Jenkins 侧引入时间戳目录,把每一次执行结果隔离。

在 Pipeline 中可以用如下方式生成时间戳变量:

def timestamp = new Date().format('yyyyMMddHHmmss') def outputDir = "result_${timestamp}"

如果你用的是 Freestyle 项目,可以通过“构建环境”里的“设置构建时间”或者安装 Timestamper 插件,配合环境变量BUILD_TIMESTAMP使用。个人更推荐 Pipeline,因为时间戳作用域清晰,后续引用方便。

3.3 标准 Pipeline 脚本:首轮执行 + 失败识别 + 重跑合并

下面给一个完整可用的 Pipeline 示例,基于--rerunfailed方案:

pipeline { agent any environment { OUTPUT_DIR = "result" // 可以改为 BUILD_TIMESTAMP 风格 } stages { stage('Checkout') { steps { checkout scm } } stage('Prepare') { steps { sh 'python3 -m venv venv || true' sh '. venv/bin/activate && pip install -r requirements.txt' } } stage('First Run') { steps { sh ''' . venv/bin/activate rm -rf ${OUTPUT_DIR} mkdir -p ${OUTPUT_DIR} robot --outputdir ${OUTPUT_DIR} --output original.xml --log original_log.html --report original_report.html tests/ ''' } } stage('Check Failed Count') { steps { sh ''' . venv/bin/activate python - <<EOF from robot.api import ExecutionResult result = ExecutionResult("${OUTPUT_DIR}/original.xml") failed = len(result.suite.all_tests) - result.suite.statistics.passed print(f"FAILED_COUNT={failed}") EOF ''' } } stage('Rerun Failed') { when { expression { return failedCount > 0 } } steps { sh ''' . venv/bin/activate robot --outputdir ${OUTPUT_DIR} --rerunfailed ${OUTPUT_DIR}/original.xml --output rerun.xml --log rerun_log.html --report rerun_report.html tests/ ''' } } stage('Merge Reports') { steps { sh ''' . venv/bin/activate rebot --outputdir ${OUTPUT_DIR} --output output.xml --log log.html --report report.html --merge ${OUTPUT_DIR}/original.xml ${OUTPUT_DIR}/rerun.xml ''' } } stage('Publish Reports') { steps { publishHTML(target: [ allowMissing: false, alwaysLinkToLastBuild: true, keepAll: true, reportDir: "${OUTPUT_DIR}", reportFiles: 'report.html', reportName: 'RobotFramework Report' ]) } } } }

这里面有几个值得解释的细节:

  • Check Failed Count阶段里使用robot.api读取 output.xml 统计失败数,是为了决定后续是否进入重跑阶段。你也可以用grep 'Test Status'这样的粗糙方式,但不推荐,因为 XML 结构在不同版本下有差异,解析 api 更稳。
  • rerun.xml只在有失败用例时才会生成。如果首轮全部通过,重跑阶段不会执行,此时合并报告阶段要处理缺少 rerun.xml 的情况。代码里我用rebot --merge original.xml rerun.xml,当 rerun.xml 不存在时,需要改用rebot --merge original.xml或直接把 original.xml 当作最终输出。这块逻辑在最终代码中建议做条件判断,避免 rebot 报错。
  • publishHTML是 Jenkins HTML Publisher 插件提供的能力,如果你们没装插件,可以用archiveArtifacts artifacts: 'result/**'把结果作为归档产物。

3.4 Freestyle 项目的变通玩法

如果团队还在用 Freestyle 项目,没用过 Pipeline,也不打算迁,那么同样可以实现失败重执行。主要思路是拆成两步:

第一步,构建(Execute shell)中执行首轮命令,然后用一个条件判断是否进入第二步。Freestyle 没有原生的 when 条件,但可以在 shell 里控制:

robot --outputdir result --output original.xml tests/ python - <<EOF from robot.api import ExecutionResult r = ExecutionResult("result/original.xml") if r.suite.statistics.failed == 0: print("ALL_PASS") else: print("NEED_RERUN") EOF

然后,再增加第二个构建步骤,执行重跑命令和合并命令,用 shell 的set -e和条件判断包裹。这种方法可行,但流程的可读性和后期维护性都不如 Pipeline。所以我个人建议新项目一律用 Pipeline,老项目改造时也可以逐步迁。

3.5 失败通知与报告归档的正确做法

重跑机制成功之后,还有一个工程细节:邮件通知不能只看最后一次构建状态。否则你会在重跑通过后收到一条“构建成功”的邮件,但实际上首轮有 15 条用例失败过。更合理的做法是在邮件中附上重跑前后失败列表:

  • 首轮失败用例:从original_log.html里解析。
  • 重跑后仍然失败的用例:从合并后的log.html或者output.xml里解析。
  • 对比两者,只把“首轮失败且重跑后仍失败”的用例视为真实缺陷推送高优先级通知。

RobotFramework 的log.html是自包含 HTML,解析不便。更推荐直接解析output.xml中的元素,例如:

from robot.api import ExecutionResult result = ExecutionResult("result/output.xml") for suite in result.suite.suites: for test in suite.tests: if test.status == 'FAIL': print(test.longname, test.message)

Jenkins 侧可以把这段解析脚本放进流水线的一个 stage,生成final_failed.txt,再用 emailext 插件发送。这样团队每天只需要关注最终失败列表即可。

4. 关键参数与配置细节深度解析

4.1--rerunfailed的筛选逻辑到底怎么工作

--rerunfailed的参数是上一个输出文件的路径。它内部会读取该 XML,找出所有状态为FAIL的测试用例的精确定位信息,比如套件路径、资源名、测试名。在执行重跑时,它只执行这些用例,其他用例完全不进入执行流程,这样速度会快很多。

注意它也能处理因为语法错误导致的套件级失败吗?语法错误会让套件整体无法执行,此时输出 XML 里可能包含 ERROR 消息,但suite.all_tests中不一定有具体失败用例。这种情况下--rerunfailed可能会选择套件重新执行,但没太大意义。因此我建议在首要执行阶段就加上--dryrun预检,把语法问题扼杀在第一轮之前。

robot --dryrun --outputdir result --output dryrun.xml tests/ robot --outputdir result --output original.xml tests/

如果 dryrun 已经发现错误,可以直接中断流水线,不用浪费时间跑第一轮完整测试。

4.2--retry参数的正确打开方式

RF 6.0+ 的--retry参数可以和--retry-dry-run结合使用。需要注意一个边界:--retry重试的条件是“测试用例执行失败”,它不会区分是 setup/teardown 失败还是步骤中断。另外重试之间的间隔默认为 0,如果你希望两次重试之间留一点喘息时间让环境恢复,可以搭配--retry-interval:

robot --retry 2 --retry-interval 10s --retry-dry-run tests/

--retry-interval支持毫秒、秒、分钟等格式,例如500ms、10s、1m。这个参数在应对环境抖动时很管用,比如浏览器进程回收导致的偶发失败,间隔 10 秒重跑一次,基本都能避开峰值期。

不过也要注意:--retry的缺陷是重试期间无法重新初始化测试环境。如果失败原因是前置测试数据被破坏,重试大概率还是失败。此时更适合用--rerunfailed方案,因为你可以先执行数据清理脚本,再进入重跑阶段。

4.3 环境变量与 Jenkins 可用变量在流水线中的传递

Jenkins Pipeline 和 RobotFramework 之间传参,建议通过环境变量。RobotFramework 中可以用%{VAR_NAME}语法访问环境变量,例如:

${base_url} %{TEST_ENV_URL}

在 Jenkins Pipeline 中这样定义:

environment { TEST_ENV_URL = "http://test.example.com" BROWSER = "headlesschrome" RETRY_TIMES = "2" }

然后在 shell 阶段直接使用这些变量。要注意 RobotFramework 的变量优先级:命令行参数 > 环境变量 > 变量文件。如果你同时在命令行传了--variable TEST_ENV_URL:xxx,环境变量会被覆盖。

4.4 输出目录结构的标准化建议

多次执行会产生多个 xml、log、report、png、txt 等文件,建议统一目录结构:

result/ ├── original.xml ├── original_log.html ├── original_report.html ├── rerun.xml ├── rerun_log.html ├── rerun_report.html ├── output.xml ├── log.html ├── report.html └── screenshots/

这样一个任务里最多跑两轮测试,每轮都保留独立的日志和报告,最终合并的则放在根部。归档策略上,Jenkins 任务建议保留最近 10-20 次构建的完整 result 目录,或者只归档合并后的output.xml。不要省空间把所有中间产物全删除,时间久了排查问题会很痛苦。

5. 实操落地:一个完整的自动化重跑示例

5.1 示例项目结构

假设有一个简单的 Web 登录测试套件,目录如下:

demo-tests/ ├── requirements.txt ├── tests/ │ ├── login.robot │ └── common.resource ├── Jenkinsfile └── scripts/ └── send_notify.py

login.robot里故意设置两个用例:一个稳定通过,一个 50% 概率失败(通过随机数),用来模拟 flaky test。

5.2 Jenkinsfile 详细版本

在上一节的标准 Pipeline 基础上,我把条件判断、结果解析、通知集成完整化:

pipeline { agent any environment { OUTPUT_DIR = "result" } stages { stage('Clean') { steps { sh 'rm -rf ${OUTPUT_DIR} && mkdir -p ${OUTPUT_DIR}' } } stage('Install Dependencies') { steps { sh ''' if [ ! -d "venv" ]; then python3 -m venv venv fi . venv/bin/activate pip install -r requirements.txt ''' } } stage('Dry Run') { steps { sh ''' . venv/bin/activate robot --dryrun --outputdir ${OUTPUT_DIR} --output dryrun.xml tests/ ''' } } stage('First Execution') { steps { sh ''' . venv/bin/activate robot --outputdir ${OUTPUT_DIR} --output original.xml --log original_log.html --report original_report.html tests/ ''' } } stage('Rerun Failed Tests') { steps { sh ''' . venv/bin/activate python - <<EOF import os, sys from robot.api import ExecutionResult original = "${OUTPUT_DIR}/original.xml" result = ExecutionResult(original) failed_count = result.suite.statistics.failed print("FAILED_COUNT=" + str(failed_count)) if failed_count > 0: os.system("robot --outputdir ${OUTPUT_DIR} --rerunfailed %s --output rerun.xml --log rerun_log.html --report rerun_report.html tests/" % original) else: print("NO_FAILED_TESTS") EOF ''' } } stage('Merge Reports') { steps { sh ''' . venv/bin/activate if [ -f "${OUTPUT_DIR}/rerun.xml" ]; then rebot --outputdir ${OUTPUT_DIR} --output output.xml --log log.html --report report.html --merge ${OUTPUT_DIR}/original.xml ${OUTPUT_DIR}/rerun.xml else rebot --outputdir ${OUTPUT_DIR} --output output.xml --log log.html --report report.html --merge ${OUTPUT_DIR}/original.xml fi ''' } } stage('Extract Final Failure List') { steps { sh ''' . venv/bin/activate python - <<EOF from robot.api import ExecutionResult result = ExecutionResult("${OUTPUT_DIR}/output.xml") failed = [] for test in result.suite.all_tests: if test.status == 'FAIL': failed.append(test.longname) print("FINAL_FAILED=" + ",".join(failed)) with open("${OUTPUT_DIR}/final_failed.txt", "w") as f: f.write("\\n".join(failed)) EOF ''' } } stage('Publish') { steps { publishHTML([ allowMissing: false, alwaysLinkToLastBuild: true, keepAll: true, reportDir: "${OUTPUT_DIR}", reportFiles: 'report.html', reportName: 'RobotFramework Combined Report' ]) archiveArtifacts artifacts: '${OUTPUT_DIR}/**', fingerprint: true } } } post { always { emailext( to: 'team@example.com', subject: "RF Test Report: ${env.BUILD_URL}", body: "Final report available at ${env.BUILD_URL}RobotFramework%20Combined%20Report/" ) } } }

5.3 如何验证重跑是否真正生效

构建完成后,我一般这样确认重跑机制真的在工作:

  • 查看构建日志中的阶段执行情况,确认Rerun Failed Tests是否执行。
  • 检查result/rerun.xml是否存在。
  • 打开合并后的report.html,确认“执行次数”列中用例数量 >1。
  • 查看output.xml中该用例的status标签属性,重跑成功的用例会显示PASS,但它可能有多个stat子标签记录首次失败与重跑成功的时间戳。

如果发现某个用例反复重跑却始终失败,那就不是环境的问题了,大概率是真实缺陷,要回到需求层面处理。

6. 常见问题与排查技巧实录

6.1 rebot 合并时提示 output.xml 中缺少关键属性

有朋友遇到过这样的报错,大致信息是AttributeError: 'TestSuite' object has no attribute 'statistics',或者合并时提示invalid output file。这通常是因为首轮执行时--output文件名或路径没写对,导致 rebot 读取到了不完整的 XML。我的排查顺序是:

  1. 确认original.xml不是空文件,大小是否合理。
  2. 用robot --version确认 RobotFramework 版本,rebot --merge对版本有一定要求,太旧的 3.x 也能用,但稳定性不如新版。
  3. 不要跨版本合并,比如首轮是 RF 5.0 生成、重跑用 RF 6.0 工具的 rebot,可能解析异常。保持 Jenkins 执行节点上同一个虚拟环境,是最省心的方式。

6.2 一边跑一边写 result 目录导致冲突

如果首轮和重跑都指向同一个输出目录,且重跑阶段还没等首轮完全释放文件句柄就去写rerun.xml,会出现文件被占用或覆盖的问题。我的建议是首轮与重跑分别用子目录隔离:

robot --outputdir result/first --output original.xml tests/ robot --outputdir result/rerun --rerunfailed result/first/original.xml --output rerun.xml tests/

最后 rebot 合并时分别指向两个目录下的 xml。这个做法更干净,也避免了日志、截图互相覆盖。

6.3 pabot 并行时重跑结果丢失

pabot 的--rerunfailed有个特点:它会把重跑任务平均分给指定进程数,如果某个进程跑完没有生成 output,最终合并可能会漏掉一部分结果。我建议 pabot 做重跑带上--output明确指定输出文件,并且最后统一 rebot。另外并行执行时如果失败用例过多,重跑阶段可能比首轮还慢,所以不妨控制重跑分片进程数略小于首轮进程数,避免资源争用。

6.4 Jenkins 控制台日志中文乱码

RobotFramework 输出中文日志时,Jenkins 控制台可能出现编码问题。这虽然不是重执行特有,但重执行日志上下文里更容易暴露。处理思路是检查 Jenkins 节点上 JVM 编码以及 shell 的locale。在 Pipeline 中显式设置环境变量比较管用:

environment { LANG = "en_US.UTF-8" LC_ALL = "en_US.UTF-8" }

6.5 重跑后报告通过率仍是旧值

合并后的report.html展示的是合并统计值,所以通过率不会因为你重跑成功而自动覆盖首轮的所有记录。它会把重跑成功的结果合并进去,最终统计通常以重跑结果为准。如果你发现通过率偏低,要注意是不是 rebot 合并时把 original 和 rerun 两份结果的统计简单叠加了。此时可以检查rebot --merge的行为版本差异,RF 较新的版本在合并时能正确处理同一用例的多次记录。

6.6 重跑阶段被误触发或未触发

如果重跑阶段明明有失败却始终没执行,多半是failedCount环境的判断出了问题。我在 Pipeline 里用内嵌 python 输出字符串后,Jenkins 通过sh返回结果捕获时,往往因为空格或者换行问题导致解析异常。建议用writeFile把计数写到文件再读取,或者干脆直接在同一个 shell 内完成判断,从流程上保证“失败则执行重跑”这一逻辑不依赖外部变量。

if grep -q '<stat pass=".*" fail="[1-9].*"' result/original.xml; then robot --rerunfailed ... fi

这种 grep 方式虽粗糙,但在 Freestyle 工程中依然实用。

7. 经验补充:边界场景与长期维护建议

7.1 失败用例重执行的优先级策略

建议把重跑策略和用例分层挂钩。比如冒烟测试(smoke)用例不允许重跑,一旦失败就立刻告警,因为冒烟测试应该高度稳定,失败说明本轮改动有重大影响;而回归测试(regression)用例允许最多一次重跑,因为回归覆盖场景宽,环境因素占比高;接口测试可以两次重跑,因为接口偶发超时比较常见。实现方式可以给用例打 tag,然后在 Jenkins 参数中控制重跑范围:

robot --rerunfailed output.xml --include smoke tests/

这条命令的意思是只重跑上一轮失败用例中带有 smoke 标签的部分。配合变量配置,团队能非常灵活地调整重跑策略。

7.2 与 Git 提交触发和定时触发的配合

如果你的 Jenkins 任务同时有 Git 提交触发和定时触发,注意避免多次任务同时执行测试,尤其是用到了共享测试环境和并行 pabot 的重跑逻辑时。建议加上“同一任务不允许并发构建”的配置,或者通过lock资源实现互斥。共享执行节点的竞争你以为少见,实际上测试环境一紧张,两个任务同时跑同一个服务的用例,失败率立刻飙升,重跑机制反而成了掩盖问题的遮羞布。

7.3 保留重跑数据用于稳定性分析

重跑机制跑一段时间之后,output.xml 里的历史执行数据会非常有价值。可以每个周末从 Jenkins 拉取所有失败用例的执行记录,统计哪些套件、哪些用例是重跑后依旧失败的验证者,以及哪些用例重跑后总是成功。这个数据能帮你定位到真实的 flaky test 黑名单,进而决定是修复、隔离还是标记跳过。RobotFramework 自身也支持--skip参数跳过特定用例(RF 5.0+),是应急处理不稳定用例的有效手段。

7.4 重执行不是因噎废食,要持续收敛失败率

最后说一个理念层面的问题。重执行机制解决的是“偶发失败干扰判断”的问题,它不能替代测试质量的持续改进。如果一个任务首轮失败率长期高于 10%,重跑只是在反复验证一个大概率会继续失败的东西,团队应当优先去分析失败根因,而不是无限提高重试次数。我在实际操作中给自己划了条线:单次执行失败率超过 15%,重跑不再自动进行,任务直接标红,强制人工介入。这么做是为了避免重执行掩盖系统性问题。

根据我个人的实践体会,失败重执行的配置本身并不复杂,难的是在这套机制中建立“失败分级处理”的思维:区分真实缺陷、环境抖动、数据污染和脚本问题,分别制定不同的响应策略。把这套思路落地到 Jenkins Pipeline 里,就能让自动化测试团队从“天天盯报告、手动重跑”的泥潭里解放出来,把精力放到真正的产品质量问题上。希望这份方案对你的项目有帮助。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进