Answer
Cucumber Parallel Execution
Method 1: TestNG DataProvider (Scenario-level Parallel)
Java
package runners;
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
import org.testng.annotations.DataProvider;
@CucumberOptions(
features = "src/test/resources/features",
glue = {"stepDefinitions", "hooks"},
plugin = {
"pretty",
"html:target/cucumber-parallel-report.html",
"json:target/cucumber-parallel.json"
},
monochrome = true
)
public class ParallelTestRunner extends AbstractTestNGCucumberTests {
@Override
@DataProvider(parallel = true) // ← THIS enables parallelism
public Object[][] scenarios() {
return super.scenarios();
}
}
testng.xml for Thread Count
XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.9.dtd">
<suite name="Cucumber Parallel Suite" parallel="methods" thread-count="5">
<test name="Parallel Cucumber Tests">
<classes>
<class name="runners.ParallelTestRunner"/>
</classes>
</test>
</suite>
Thread-Safe WebDriver with PicoContainer
Java
// ScenarioContext.java — new instance per scenario (PicoContainer guarantees this)
public class ScenarioContext {
public WebDriver driver; // each scenario gets its OWN instance
}
// Hooks.java
public class Hooks {
private final ScenarioContext ctx;
public Hooks(ScenarioContext ctx) { this.ctx = ctx; }
@Before
public void setUp() {
WebDriverManager.chromedriver().setup();
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless=new", "--no-sandbox", "--disable-dev-shm-usage");
ctx.driver = new ChromeDriver(options); // each thread gets its own driver
ctx.driver.manage().window().maximize();
}
@After
public void tearDown(Scenario scenario) {
if (scenario.isFailed()) {
byte[] screenshot = ((TakesScreenshot) ctx.driver)
.getScreenshotAs(OutputType.BYTES);
scenario.attach(screenshot, "image/png", "Failure");
}
ctx.driver.quit();
}
}
Method 2: Feature-Level Parallel with Multiple Runners
Java
// Runner1.java — runs login.feature
@CucumberOptions(features = "src/test/resources/features/login.feature", ...)
public class Runner1 extends AbstractTestNGCucumberTests {}
// Runner2.java — runs checkout.feature
@CucumberOptions(features = "src/test/resources/features/checkout.feature", ...)
public class Runner2 extends AbstractTestNGCucumberTests {}
XML
<!-- Run both in parallel -->
<suite name="Feature Parallel" parallel="tests" thread-count="2">
<test name="Login">
<classes><class name="runners.Runner1"/></classes>
</test>
<test name="Checkout">
<classes><class name="runners.Runner2"/></classes>
</test>
</suite>
Method 3: Cucumber-JVM-Parallel-Plugin (Older Projects)
XML
<plugin>
<groupId>com.github.temyers</groupId>
<artifactId>cucumber-jvm-parallel-plugin</artifactId>
<version>5.0.0</version>
<executions>
<execution>
<goals><goal>generateRunners</goal></goals>
<configuration>
<parallelScheme>SCENARIO</parallelScheme>
<glue>stepDefinitions</glue>
</configuration>
</execution>
</executions>
</plugin>
Common Parallel Issues and Fixes
| Issue | Cause | Fix |
|---|---|---|
| Driver NullPointerException | Sharing driver across threads | Use PicoContainer DI |
| Screenshots missing | Wrong driver reference | Always use ctx.driver |
| Flaky test results | Race condition in static fields | Remove all static state |
| Report missing scenarios | Concurrent file write | Use JSON + merge plugin |
Merging Parallel Reports
XML
<!-- masterthought plugin merges parallel JSON reports -->
<plugin>
<groupId>net.masterthought</groupId>
<artifactId>maven-cucumber-reporting</artifactId>
<version>5.8.1</version>
<executions>
<execution>
<goals><goal>generate</goal></goals>
<configuration>
<projectName>AutomateQA</projectName>
<outputDirectory>target/cucumber-html-reports</outputDirectory>
<jsonFiles>
<param>target/**/*.json</param>
</jsonFiles>
</configuration>
</execution>
</executions>
</plugin>
