Cucumber JVM Parallel插件并行执行及线程安全报告实现求助
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
Configure Parallel Plugin to Output Thread-Isolated JSON Reports
Update yourpom.xmlto 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>Merge JSON Reports into a Single HTML Report
Use themaven-cucumber-reportingplugin (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
Create a Thread-Safe Extent Listener
Write a Cucumber listener that usesThreadLocalto manage individualExtentTestinstances 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(); } }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 {}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
Add Dependency to pom.xml
<dependency> <groupId>com.aventstack</groupId> <artifactId>extentreports-cucumber7-adapter</artifactId> <version>1.11.0</version> <scope>test</scope> </dependency>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 {}Add
extent.propertiesto Resources
Create asrc/test/resources/extent.propertiesfile 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

