如何实现测试脚本失败且输出含特定文本时自动多次重跑?
解决不稳定测试的自动重跑问题
需求背景
我们有部分不稳定测试偶尔失败,正在排查根因。这类失败的测试输出中固定出现文本The current node list is empty。需要实现:
- 测试失败且输出包含该文本时,自动重跑多次
- 测试失败但输出不含该文本,直接标记失败
原方案问题
尝试的重复执行命令虽然能在匹配文本出现时重跑,但即使测试失败也会被判定为成功——原因是ack --passthru在找到匹配文本时返回0,覆盖了pest测试的退出码,导致整个管道的退出码被ack的结果替换。
原尝试的命令(注意最后一行缺少闭合引号):
docker compose --project-directory ./app --env-file ./.env exec php vendor/bin/pest --coverage-clover=clover.xml | ack --passthru --color "The current node list is empty" && docker compose --project-directory ./app --env-file ./.env exec php vendor/bin/pest --coverage-clover=clover.xml | ack --passthru --color "The current node list is empty" && docker compose --project-directory ./app --env-file ./.env exec php vendor/bin/pest --coverage-clover=clover.xml | ack --passthru --color "The current node list is empty" && docker compose --project-directory ./app --env-file ./.env exec php vendor/bin/pest --coverage-clover=clover.xml | ack --passthru --color "The current node list is empty" && docker compose --project-directory ./app --env-file ./.env exec php vendor/bin/pest --coverage-clover=clover.xml | ack --passthru --color "The current node list is empty" && docker compose --project-directory ./app --env-file ./.env exec php vendor/bin/pest --coverage-clover=clover.xml | ack --passthru --color "The current node list is empty" && docker compose --project-directory ./app --env-file ./.env exec php vendor/bin/pest --coverage-clover=clover.xml | ack --passthru --color "The current node list is empty" && docker compose --project-directory ./app --env-file ./.env exec php vendor/bin/pest --coverage-clover=clover.xml | ack --passthru --color "The current node list is empty" && docker compose --project-directory ./app --env-file ./.env exec php vendor/bin/pest --coverage-clover=clover.xml | ack --passthru --color "The current node list is empty" && docker compose --project-directory ./app --env-file ./.env exec php vendor/bin/pest --coverage-clover=clover.xml | ack --passthru --color "The current node list is empty
解决方案
方法1:封装测试逻辑,精准控制退出码
把单次测试逻辑封装成函数,单独捕获测试的退出码和输出,再判断是否需要重跑:
# 封装单次测试逻辑 run_test() { # 执行测试,保存输出到临时文件,捕获原退出码 docker compose --project-directory ./app --env-file ./.env exec php vendor/bin/pest --coverage-clover=clover.xml > test_output.txt 2>&1 local test_exit_code=$? # 输出内容到终端,同时用ack高亮目标文本 cat test_output.txt | ack --passthru --color "The current node list is empty" # 判定逻辑:测试失败且含目标文本时返回特殊码(100),否则返回原退出码 if [ $test_exit_code -ne 0 ]; then if grep -q "The current node list is empty" test_output.txt; then return 100 fi fi return $test_exit_code } # 循环执行测试,最多重跑10次 max_retries=10 retry_count=0 final_exit_code=0 while [ $retry_count -lt $max_retries ]; do run_test final_exit_code=$? # 不是需要重跑的情况,直接退出循环 if [ $final_exit_code -ne 100 ]; then break fi retry_count=$((retry_count + 1)) echo "检测到目标错误,开始第 $retry_count 次重跑..." done # 返回最终退出码 exit $final_exit_code
方法2:用grep替代ack实现高亮+逻辑控制
如果不想依赖ack,用grep也能实现文本高亮和退出码控制:
run_test() { # 捕获测试输出和退出码 local test_output test_output=$(docker compose --project-directory ./app --env-file ./.env exec php vendor/bin/pest --coverage-clover=clover.xml 2>&1) local test_exit_code=$? # 高亮输出目标文本(grep的-E参数匹配所有行+目标文本) echo "$test_output" | grep --color=always -E "(The current node list is empty|.*)" # 重跑判定逻辑和方法1一致 if [ $test_exit_code -ne 0 ]; then if echo "$test_output" | grep -q "The current node list is empty"; then return 100 fi fi return $test_exit_code } # 循环调用部分和方法1完全相同 max_retries=10 retry_count=0 final_exit_code=0 while [ $retry_count -lt $max_retries ]; do run_test final_exit_code=$? if [ $final_exit_code -ne 100 ]; then break fi retry_count=$((retry_count + 1)) echo "检测到目标错误,开始第 $retry_count 次重跑..." done exit $final_exit_code
核心逻辑说明
- 单独捕获测试命令的退出码,避免被后续文本处理命令的退出码覆盖
- 用特殊退出码(如100)标记「需要重跑」的场景,和「真正测试失败」的场景区分开
- 循环逻辑只在收到「需要重跑」的退出码时才执行下一次测试
内容的提问来源于stack exchange,提问作者TobyG
相关产品推荐
相关产品推荐

