Q15 of 40 · Karate
How does the Karate parallel runner work? What's the unit of parallelism?
Short answer
Short answer: Karate's parallel runner (Runner.path().parallel(threadCount).execute()) runs feature files concurrently — the unit of parallelism is the feature file, not the scenario. Scenarios within a single feature always run sequentially on the same thread. Each thread gets a fresh Karate context with independent variables.
Detail
Running the parallel suite (JUnit 5):
@Test
void runSuite() {
Results results = Runner
.path("classpath:features")
.outputJunitXml(true)
.parallel(4);
assertThat(results.getFailCount()).isZero();
}
Unit of parallelism — feature file: features/users/create-user.feature runs on thread 1 while features/orders/create-order.feature runs on thread 2. Scenarios within a feature run sequentially on the assigned thread — Karate preserves scenario ordering within a file.
Why this design: scenarios within a feature often share Background state and may depend on the order established by the Background block. Forcing them to different threads would break that contract.
Thread-local context: each parallel thread has its own karate object, variable scope, and HTTP client instance. Variables defined in one thread's feature cannot bleed into another thread.
Shared state risks (still your responsibility):
- Database rows shared across feature files (create in file A, delete in file B)
- Global WireMock stubs not scoped per thread
- External files written by one test and read by another
Tuning thread count: start with the number of CPU cores or the server's connection pool size — whichever is smaller. Measure total run time as you increase; there's a diminishing return point.
// EXAMPLE
SuiteRunner.java
import com.intuit.karate.junit5.Karate;
import com.intuit.karate.Results;
import com.intuit.karate.Runner;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class SuiteRunner {
@Test
void runAllFeatures() {
Results results = Runner
.path("classpath:features") // all .feature files under this path
.tags("~@ignore") // skip features tagged @ignore
.outputJunitXml(true) // CI-compatible XML output
.outputCucumberJson(true) // for Cucumber reports
.parallel(4); // 4 concurrent threads
// Assert in the JUnit test — will fail the build if any scenario fails
assertThat(results.getFailCount())
.as("Expected zero failures but got %d:
%s",
results.getFailCount(), results.getErrorMessages())
.isZero();
}
}