Parallel Execution
Run tests in parallel to dramatically cut execution time. Karate's core parallel feature operates independently of build tools and provides comprehensive reporting for CI/CD integration.
When running in parallel, scenarios execute in any order and must be completely isolated from each other.
Critical rules for parallel execution:
- Each scenario must be runnable independently
- Don't share state between scenarios via Background variables
- Don't depend on execution order
- You should be able to comment out any scenario without breaking others
Test your isolation: Run scenarios individually and ensure they all pass independently.
Why this matters: Parallel execution runs scenarios on different threads simultaneously. Shared state or execution order dependencies will cause random, difficult-to-debug failures.
Benefits of Parallel Execution
- 5-10x faster execution for large test suites
- Zero configuration required - works out of the box
- Multiple levels - Features and scenarios run in parallel
- Comprehensive reporting - HTML, JUnit XML, Cucumber JSON
Basic Parallel Runner
The simplest way to run tests in parallel using the Runner API:
import com.intuit.karate.Results;
import com.intuit.karate.Runner;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class ParallelTest {
@Test
void testParallel() {
Results results = Runner.path("classpath:features")
.tags("~@ignore")
.parallel(5); // 5 threads
assertEquals(0, results.getFailCount(), results.getErrorMessages());
}
}
Key points:
parallel()
must be the last method called- Returns
Results
object with execution details - Thread count determines parallelism level
Runner.Builder Configuration
Path Selection
// Single package
Runner.path("classpath:features")
// Multiple packages
Runner.path("classpath:api", "classpath:integration")
// Mix packages and individual features
Runner.path("classpath:smoke", "classpath:critical/login.feature")
// Dynamic paths
List<String> paths = Arrays.asList("classpath:users", "classpath:products");
Runner.path(paths)
Tag Filtering
// Include specific tags
.tags("@smoke") // Only smoke tests
.tags("@api", "@smoke") // AND operation - must have both
.tags("@smoke,@critical") // OR operation - either tag
// Exclude tags
.tags("~@slow") // Exclude slow tests
.tags("~@ignore", "~@manual") // Exclude multiple
// Complex combinations
.tags("(@smoke or @critical) and not @flaky")
Report Configuration
Results results = Runner.path("classpath:features")
.outputJunitXml(true) // For CI/CD integration
.outputCucumberJson(true) // For dashboards
.reportDir("target/karate-reports") // Custom directory
.karateEnv("staging") // Set environment
.systemProperty("api.timeout", "30000") // Pass properties
.parallel(8); // Execute with 8 threads
Execution Statistics
After running tests, Karate provides detailed execution statistics:
======================================================
elapsed: 2.35 | threads: 5 | thread time: 4.98
features: 54 | ignored: 25 | efficiency: 0.42
scenarios: 145 | passed: 145 | failed: 0
======================================================
Understanding the metrics:
- elapsed: Total wall clock time
- efficiency: How well threads were utilized (higher is better)
- thread time: Total CPU time across all threads
- features/scenarios: Execution and skip counts
Parallel Behavior
Multi-Level Parallelism
Karate runs tests in parallel at multiple levels:
- Features run in parallel across threads
- Scenarios within features run in parallel
- Scenario Outline examples run in parallel
Controlling Parallelism
Use @parallel=false
to force sequential execution when needed:
# Force entire feature to run sequentially
@parallel=false
Feature: Database setup
# All scenarios run one after another
# Control individual scenarios
Feature: Mixed execution
Scenario: Runs in parallel * print 'Parallel execution'
@parallel=false
Scenario: Runs sequentially * print 'Sequential execution'
Sequential execution usually indicates test dependencies. Design independent tests instead of using @parallel=false
.
Thread Count Optimization
Environment-Based Threading
private int getOptimalThreads() {
String env = System.getProperty("karate.env", "dev");
int cores = Runtime.getRuntime().availableProcessors();
switch (env) {
case "dev": return Math.min(cores, 4); // Don't overwhelm dev machine
case "ci": return Math.min(cores, 6); // CI resource constraints
case "perf": return cores * 2; // Maximize for load testing
default: return cores;
}
}
Resource Considerations
- CPU cores: Good starting point is 1-2x CPU cores
- Memory: ~256MB per thread for typical tests
- Network: Consider external service limits (e.g., API rate limits)
- CI environment: Usually 2-6 threads work best
Tuning Thread Count
To optimize thread count:
- Start with
availableProcessors()
or 1-2x CPU cores. - Monitor
efficiency
in execution statistics (aim for >0.7). - Use tools like
htop
orVisualVM
to check CPU/memory usage. - Adjust based on test suite size and external service limits (e.g., reduce threads if hitting API rate limits).
Debugging Parallel Failures
If tests fail in parallel:
- Check
results.getErrorMessages()
for detailed error logs per scenario. - Review
karate-timeline.html
to identify slow or failed tests by thread. - Enable
karate.log
for verbose logging:.systemProperty("log.level", "DEBUG")
. - Isolate flaky tests by running with fewer threads or
@parallel=false
.
Designing Tests for Parallel Execution
To maximize parallelism:
- Independent Tests: Ensure scenarios don’t share state (e.g., avoid global variables or shared files).
- Test Data: Use unique data per scenario (e.g., generate random IDs with
karate.uuid()
). - API Rate Limits: Implement retries with
karate.retry()
for external service constraints. - Feature Structure: Group related scenarios in features to optimize thread distribution.
Advanced Patterns
Environment-Specific Execution
@Test
void environmentOptimized() {
String env = System.getProperty("karate.env", "dev");
Results results = Runner.path("classpath:features")
.tags(getEnvironmentTags(env))
.karateEnv(env)
.parallel(getThreadCount(env));
assertEquals(0, results.getFailCount());
}
private String[] getEnvironmentTags(String env) {
if ("prod".equals(env)) {
return new String[]{"@smoke", "~@experimental"};
}
return new String[]{"~@ignore"};
}
Feature-Specific Threading
@Test
void optimizedByFeatureType() {
// Fast API tests with high parallelism
Results apiResults = Runner.path("classpath:api")
.tags("@api", "@fast")
.parallel(12);
// Database tests with moderate parallelism
Results dbResults = Runner.path("classpath:database")
.tags("@database")
.parallel(4);
assertEquals(0, apiResults.getFailCount());
assertEquals(0, dbResults.getFailCount());
}
Report Generation
Timeline Visualization
Karate automatically generates karate-timeline.html
for visual analysis:
- Thread utilization - How effectively threads are used
- Performance bottlenecks - Identify slow tests
- Load balancing - Distribution across threads
CI/CD Integration
@Test
void ciOptimized() {
Results results = Runner.path("classpath:features")
.tags("@regression", "~@slow")
.outputJunitXml(true) // Jenkins/CI integration
.outputCucumberJson(true) // Dashboard integration
.reportDir("target/ci-reports")
.parallel(4); // Conservative for CI
assertEquals(0, results.getFailCount(), results.getErrorMessages());
}
CI Tool Example (GitHub Actions)
Common Gotchas
- Thread count too high: Leads to resource exhaustion and poor efficiency
- Test dependencies: Scenarios must be independent to run in parallel
- Shared state: Avoid global variables or shared files between tests
- CI constraints: Use fewer threads in CI environments than locally
Next Steps
Maximize parallel execution effectiveness:
- Tags - Organize tests for optimal parallel execution
- Test Reports - Analyze parallel execution results
- Configuration - Environment-specific settings