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

Karate自动化测试:如何实现失败用例多线程并行重试?

如何让Karate测试的重试任务并行执行?

我们在测试运行中实现了重试机制,但发现重试仅在单线程中执行。有没有办法让所有失败测试用例以并行模式执行重试?

以下是当前的Testrunner.java代码:

void Runner() {
    int threadCount = getThreadCountProperty();
    Results results = karateRunner(FEATURE_TEST_PATH, ignoredTags, REPORT_DIR).parallel(threadCount);
    List<ScenarioResult> failed = results.getScenarioResults().filter(sr -> sr.isFailed()).collect(Collectors.toList());

    if (failed.isEmpty()) {
        KARATE_LOGGER.info("No failed case found");
    } else {
        KARATE_LOGGER.info(String.format("Found %d failed case(s)", failed.size()));
        for (ScenarioResult f : failed) {
            Scenario retryScenario = f.getScenario();
            try {
                for (int i = 0; i < MAX_RETRY; i++) {
                    KARATE_LOGGER.info(String.format("Retry Attempt: %d - %s", i, retryScenario.getName()));
                    ScenarioResult retryResult = results.getSuite().retryScenario(retryScenario);
                    if (!retryResult.isFailed()) {
                        assertFalse(retryResult.isFailed());
                        results = results.getSuite().updateResults(retryResult);
                        break;
                    }
                }
            } catch (NullPointerException e) {
                System.err.println("No more testcases found");
            }
            finally {
                /**
                 * Remove this remark to generate html cucumber-report
                 */
                // generateReport(results.getReportDir());
                assertEquals(0, results.getFailCount(), results.getErrorMessages());
            }
        }
    }
}

能否让重试任务分配到所有线程执行?


解决方案:用线程池实现并行重试

原代码的问题在于逐个遍历失败场景并串行执行重试,导致只能单线程运行。可以通过Java的ExecutorService和CompletableFuture实现多线程并行重试,复用原测试的线程数配置。

修改后的代码如下:

void Runner() {
    int threadCount = getThreadCountProperty();
    Results results = karateRunner(FEATURE_TEST_PATH, ignoredTags, REPORT_DIR).parallel(threadCount);
    List<ScenarioResult> failed = results.getScenarioResults().filter(sr -> sr.isFailed()).collect(Collectors.toList());

    if (failed.isEmpty()) {
        KARATE_LOGGER.info("No failed case found");
    } else {
        KARATE_LOGGER.info(String.format("Found %d failed case(s)", failed.size()));
        
        // 创建固定大小线程池,复用原测试线程数
        try (ExecutorService executor = Executors.newFixedThreadPool(threadCount)) {
            // 提交所有重试任务到线程池
            List<CompletableFuture<Void>> retryFutures = failed.stream()
                    .map(failedScenario -> CompletableFuture.runAsync(() -> {
                        Scenario scenario = failedScenario.getScenario();
                        for (int attempt = 0; attempt < MAX_RETRY; attempt++) {
                            KARATE_LOGGER.info(String.format("Retry Attempt: %d - %s (Thread: %s)",
                                    attempt + 1, scenario.getName(), Thread.currentThread().getName()));
                            ScenarioResult retryResult = results.getSuite().retryScenario(scenario);
                            
                            if (!retryResult.isFailed()) {
                                // 线程安全更新测试结果
                                synchronized (results) {
                                    results = results.getSuite().updateResults(retryResult);
                                }
                                break;
                            }
                        }
                    }, executor))
                    .collect(Collectors.toList());
            
            // 等待所有重试任务完成
            CompletableFuture.allOf(retryFutures.toArray(new CompletableFuture[0])).join();
        } catch (Exception e) {
            KARATE_LOGGER.error("Error during parallel retry", e);
        }
        
        // 所有重试完成后统一验证结果、生成报告
        // generateReport(results.getReportDir());
        assertEquals(0, results.getFailCount(), results.getErrorMessages());
    }
}

关键修改点说明

  1. 线程池复用:使用Executors.newFixedThreadPool(threadCount)创建和原测试相同线程数的线程池,保证重试的并行度和原测试一致。
  2. 并行任务提交:通过CompletableFuture.runAsync将每个失败场景的重试逻辑提交到线程池,实现多线程并行执行。
  3. 线程安全处理:在更新results时添加synchronized同步块,避免多线程同时更新导致的竞态条件(如果Karate的updateResults方法本身线程安全,可省略此同步块)。
  4. 统一结果验证:将断言和报告生成逻辑移到所有重试任务完成后,确保所有重试执行完毕再做最终校验。
  5. 调试信息:添加线程名称打印,方便确认重试任务确实在多线程中执行。

注意事项

  • 确认Karate的retryScenario方法支持多线程调用,不会出现资源冲突或状态异常。
  • 如果重试场景依赖共享资源(如测试环境、数据库连接),需额外做好资源的线程安全控制。
  • 线程池使用try-with-resources语法,确保任务完成后自动关闭,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 16:27:42