You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gradle Test Retry搭配Serenity Test:如何统一合并测试结果?

解决Serenity Test与gradle-test-retry插件的结果覆盖问题

问题核心:org.gradle.test-retry插件的重试操作会被Serenity视为独立执行,每次重试都会生成新的serenityTest.json覆盖原有文件,导致最终报告只保留最后一次重试的结果,且默认aggregate任务无法自动合并这些分散的结果。

以下是两种可行的解决思路:

方案一:自定义结果合并任务(推荐)

通过将主执行和每次重试的结果输出到独立目录,再编写自定义任务合并所有结果,确保aggregate能读取到完整的测试数据。

修改build.gradle配置

plugins {
  id "net.serenity-bdd.serenity-gradle-plugin" version "4.2.34"
  id 'java'
  id 'org.gradle.test-retry' version "1.6.2"
}

tasks.register('e2e-test', Test) {
  // 原有测试配置(如testClassesDirs、classpath等)
  
  gradle.startParameter.setContinueOnFailure(true)
  
  // 主执行的结果输出到独立目录
  systemProperty 'serenity.outputDirectory', "build/reports/serenity/main"
  
  retry {
    if (System.getenv("GITLAB_CI")) {
      maxRetries = 3
      maxFailures = 10
    }
    failOnPassedAfterRetry = false
    
    // 每次重试输出到不同子目录,避免覆盖
    onRetry { attempt ->
      systemProperty 'serenity.outputDirectory', "build/reports/serenity/retry-${attempt}"
    }
  }
  
  // 先执行合并任务,再执行aggregate
  finalizedBy(mergeSerenityResults, aggregate)
}

// 自定义合并任务:合并所有重试目录的测试结果
tasks.register('mergeSerenityResults') {
  dependsOn('e2e-test')
  
  doLast {
    def baseDir = file("build/reports/serenity")
    def mainDir = baseDir.dir("main")
    def retryDirs = baseDir.listFiles().findAll { it.name.startsWith("retry-") && it.isDirectory() }
    
    def mergedResults = []
    def jsonSlurper = new groovy.json.JsonSlurper()
    
    // 读取主执行的结果
    def mainJson = mainDir.file("serenityTest.json")
    if (mainJson.exists()) {
      mergedResults.addAll(jsonSlurper.parse(mainJson))
    }
    
    // 读取并合并重试结果:用最新的重试结果覆盖之前失败的测试
    retryDirs.each { dir ->
      def retryJson = dir.file("serenityTest.json")
      if (retryJson.exists()) {
        def retryTests = jsonSlurper.parse(retryJson)
        retryTests.each { retryTest ->
          def existingIdx = mergedResults.findIndexOf { it.testId == retryTest.testId }
          if (existingIdx != -1) {
            mergedResults[existingIdx] = retryTest
          } else {
            mergedResults.add(retryTest)
          }
        }
      }
    }
    
    // 将合并后的结果写入aggregate默认读取的目录
    def aggregateTargetDir = baseDir
    aggregateTargetDir.mkdirs()
    new File(aggregateTargetDir, "serenityTest.json").write(
      new groovy.json.JsonBuilder(mergedResults).toPrettyString()
    )
    
    // 复制所有报告资源(截图、HTML片段等)到目标目录
    [mainDir, *retryDirs].each { sourceDir ->
      copy {
        from sourceDir
        into aggregateTargetDir
        include "**/*"
        exclude "serenityTest.json"
      }
    }
  }
}

方案说明

  1. 主执行和每次重试的结果分别输出到main和retry-N目录,彻底避免文件覆盖
  2. 合并任务会将所有重试的测试结果与主执行结果合并,用重试的最新结果覆盖原有失败的测试记录
  3. 最后将合并后的结果和所有报告资源统一放到aggregate任务默认读取的目录,确保生成完整报告

方案二:配置aggregate任务读取所有重试目录(简洁版)

如果Serenity的aggregate任务支持通配符路径,可以直接配置其读取所有重试目录的结果,无需自定义合并任务。

修改build.gradle配置

plugins {
  id "net.serenity-bdd.serenity-gradle-plugin" version "4.2.34"
  id 'java'
  id 'org.gradle.test-retry' version "1.6.2"
}

tasks.register('e2e-test', Test) {
  // 原有测试配置
  
  gradle.startParameter.setContinueOnFailure(true)
  
  systemProperty 'serenity.outputDirectory', "build/reports/serenity/main"
  
  retry {
    if (System.getenv("GITLAB_CI")) {
      maxRetries = 3
      maxFailures = 10
    }
    failOnPassedAfterRetry = false
    
    onRetry { attempt ->
      systemProperty 'serenity.outputDirectory', "build/reports/serenity/retry-${attempt}"
    }
  }
  
  finalizedBy(aggregate)
}

// 配置aggregate任务读取所有主执行和重试目录的结果
tasks.named('aggregate') {
  dependsOn('e2e-test')
  // 用逗号分隔所有需要聚合的目录,支持通配符(需确认Serenity版本兼容性)
  systemProperty 'serenity.aggregate.source', 'build/reports/serenity/main,build/reports/serenity/retry-*'
}

注意事项

  • 部分Serenity版本可能不支持通配符路径,若此方案无效,请切换到方案一
  • Serenity的aggregate任务会自动处理重复的测试用例,保留最新的执行结果

内容的提问来源于stack exchange,提问作者Ragnarsson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 09:24:51