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

Cucumber JVM Parallel插件并行执行及线程安全报告实现求助

Thread-Safe Reporting for Cucumber Parallel Execution (Surefire + Cucumber JVM Parallel Plugin)

Hey there! As someone who's tackled this exact problem with Cucumber parallel execution and thread-safe reporting, let me walk you through the most reliable GitHub-inspired solutions I've used and seen in production projects:

Core Issue Breakdown

The root problem with Extent Reports/ExtendedCucumberOptions in parallel mode is that they often rely on shared, non-thread-safe report instances or single-file output. Multiple threads writing to the same resource causes race conditions, leading to missing reports or corrupted data.


Solution 1: Use Cucumber's Native JSON Reports + Post-Test Merging (Most Stable)

This is the go-to approach in most GitHub projects because it leverages Cucumber's built-in thread-safe output and a dedicated merging tool.

Step-by-Step Implementation

  1. Configure Parallel Plugin to Output Thread-Isolated JSON Reports
    Update your pom.xml to make the Cucumber JVM Parallel Plugin generate separate JSON reports for each thread (stored in unique directories):

    <plugin>
        <groupId>com.github.temyers</groupId>
        <artifactId>cucumber-jvm-parallel-plugin</artifactId>
        <version>5.0.0</version>
        <executions>
            <execution>
                <id>generate-runners</id>
                <phase>generate-test-sources</phase>
                <goals>
                    <goal>generateRunners</goal>
                </goals>
                <configuration>
                    <glue>com.your.stepdef.package</glue>
                    <featuresDirectory>src/test/resources/features</featuresDirectory>
                    <outputDirectory>target/generated-test-sources/cucumber</outputDirectory>
                    <parallelScheme>FEATURE</parallelScheme> <!-- or SCENARIO for finer granularity -->
                    <useTestNG>false</useTestNG> <!-- Set to true if using TestNG -->
                    <format>json</format>
                    <!-- Pass thread ID to output unique report paths -->
                    <customVmArgs>-Dcucumber.reporting.output.dir=target/cucraw-reports/thread-${thread.id}</customVmArgs>
                </configuration>
            </execution>
        </executions>
    </plugin>
    
  2. Merge JSON Reports into a Single HTML Report
    Use the maven-cucumber-reporting plugin (maintained on GitHub) to aggregate all thread-specific JSON files into a single, clean report:

    <plugin>
        <groupId>net.masterthought</groupId>
        <artifactId>maven-cucumber-reporting</artifactId>
        <version>5.7.0</version>
        <executions>
            <execution>
                <id>merge-reports</id>
                <phase>post-integration-test</phase>
                <goals>
                    <goal>generate</goal>
                </goals>
                <configuration>
                    <projectName>Your Project Name</projectName>
                    <outputDirectory>target/cucumber-reports/final</outputDirectory>
                    <cucumberOutput>target/cucraw-reports/**/cucumber.json</cucumberOutput> <!-- Glob pattern to match all thread reports -->
                    <buildNumber>1.0.0</buildNumber>
                </configuration>
            </execution>
        </executions>
    </plugin>
    

Solution 2: Thread-Safe Extent Reports with ThreadLocal

If you specifically need Extent Reports, many GitHub projects use ThreadLocal to isolate report instances per thread, then combine the results.

Step-by-Step Implementation

  1. Create a Thread-Safe Extent Listener
    Write a Cucumber listener that uses ThreadLocal to manage individual ExtentTest instances for each thread:

    import com.aventstack.extentreports.ExtentReports;
    import com.aventstack.extentreports.ExtentTest;
    import com.aventstack.extentreports.reporter.ExtentSparkReporter;
    import io.cucumber.java.After;
    import io.cucumber.java.Before;
    import io.cucumber.java.Scenario;
    
    public class ThreadSafeExtentListener {
        private static final ThreadLocal<ExtentTest> threadLocalTest = new ThreadLocal<>();
        private static ExtentReports extent;
    
        @BeforeAll
        public static void initReport() {
            // Create a unique reporter per thread (or use a single reporter with thread-safe entries)
            ExtentSparkReporter sparkReporter = new ExtentSparkReporter(
                "target/extent-reports/thread-" + Thread.currentThread().getId() + ".html"
            );
            extent = new ExtentReports();
            extent.attachReporter(sparkReporter);
        }
    
        @Before
        public void startScenario(Scenario scenario) {
            ExtentTest test = extent.createTest(scenario.getName());
            threadLocalTest.set(test);
        }
    
        @After
        public void endScenario(Scenario scenario) {
            ExtentTest currentTest = threadLocalTest.get();
            if (scenario.isFailed()) {
                currentTest.fail("Scenario failed: " + scenario.getStatus());
            } else {
                currentTest.pass("Scenario passed: " + scenario.getStatus());
            }
            threadLocalTest.remove(); // Clean up thread-local storage
        }
    
        @AfterAll
        public static void flushReport() {
            extent.flush();
        }
    }
    
  2. Register the Listener in CucumberOptions
    Add the listener to your Cucumber runner class:

    import io.cucumber.junit.Cucumber;
    import io.cucumber.junit.CucumberOptions;
    import org.junit.runner.RunWith;
    
    @RunWith(Cucumber.class)
    @CucumberOptions(
        features = "src/test/resources/features",
        glue = "com.your.stepdef.package",
        plugin = {"com.your.listener.package.ThreadSafeExtentListener"}
    )
    public class TestRunner {}
    
  3. Optional: Merge Thread-Specific Extent Reports
    If you want a single report instead of multiple thread files, use Extent Reports' built-in merging utility or a custom script to combine the HTML outputs.


Solution 3: Use Extent Reports' Official Cucumber Adapter (Cucumber 7+)

For Cucumber 7 and above, the official extentreports-cucumber7-adapter (hosted on GitHub) natively supports parallel execution with thread-safe reporting.

Step-by-Step Implementation

  1. Add Dependency to pom.xml

    <dependency>
        <groupId>com.aventstack</groupId>
        <artifactId>extentreports-cucumber7-adapter</artifactId>
        <version>1.11.0</version>
        <scope>test</scope>
    </dependency>
    
  2. Configure the Adapter in CucumberOptions

    @RunWith(Cucumber.class)
    @CucumberOptions(
        features = "src/test/resources/features",
        glue = "com.your.stepdef.package",
        plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"}
    )
    public class TestRunner {}
    
  3. Add extent.properties to Resources
    Create a src/test/resources/extent.properties file to configure report settings:

    extent.reporter.spark.start=true
    extent.reporter.spark.out=target/extent-reports/SparkReport.html
    extent.reporter.spark.config=src/test/resources/extent-config.xml
    extent.project.name=Your Project Name
    

This adapter automatically handles thread isolation and merges all test results into a single report without extra setup.


Final Recommendations

  • For stability: Go with Solution 1 (JSON + merging) – it's battle-tested in most GitHub projects.
  • For Extent Reports lovers: Use Solution 3 if you're on Cucumber 7+; otherwise, use Solution 2.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:53:21