Skip to main content

Parallel Execution

TestFly supports parallel test execution out of the box. Configure the thread count in testfly.yml and the framework handles thread-safe driver management automatically.


Configuration​

testfly.yml
execution:
mode: local
parallel: methods # none (default) | methods | classes | tests | instances
threadCount: 4 # number of concurrent browser sessions
maxActiveSessions: 4 # semaphore cap on concurrent browsers

browser:
name: chrome

timeouts:
explicit: 10
pageLoad: 30

parallel, threadCount and maxActiveSessions all live under execution: — see the Configuration Reference. timeouts.explicit and timeouts.pageLoad are required by every testfly.yml, parallel or not.

maxActiveSessions acts as a hard ceiling on concurrent browsers. If threadCount is 4 but maxActiveSessions is 2, at most 2 browsers will run at the same time and TestFly logs a startup warning. The other threads wait for a free slot for up to execution.sessionWaitSeconds (default 300), so set maxActiveSessions to at least threadCount.


TestNG suite file​

Parallel execution requires a TestNG suite XML file:

testng.xml
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="TestFly" parallel="methods" thread-count="4">
<test name="All Tests">
<classes>
<class name="com.example.tests.LoginTest"/>
<class name="com.example.tests.CheckoutTest"/>
<class name="com.example.tests.SearchTest"/>
</classes>
</test>
</suite>

Run it with Maven Surefire:

pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>

How thread safety works​

ConcernHow TestFly handles it
WebDriver instanceThreadLocal<WebDriver> — each thread has its own driver
Config accessAtomicReference — reads are lock-free
Step recordingConcurrentHashMap keyed by test ID
Session limitingSemaphore (fair) — acquired before driver create, released after quit
Screenshot captureReads driver from ThreadLocal — always the right instance

You do not need to do anything special in your tests. getDriver() always returns the driver for the calling thread.


Parallel modes​

execution.parallel is validated at suite bootstrap, before any test runs, against TestNG's own set of parallel modes. An unrecognised value fails immediately with a message naming both the rejected value and the accepted ones.

ModeDescriptionRecommended
noneSequential execution (default)
methodsEach test method runs in its own threadBest general choice
classesEach test class runs in a threadUse when tests within a class must be sequential
testsEach <test> in the suite XML runs in a threadUse to isolate suite-level groupings
instancesEach test class instance runs in a threadRarely needed — factory-driven suites

Parallel + per-suite lifecycle​

When browser.lifecycle: per-suite is combined with parallel execution, each thread opens its own browser once and reuses it for all tests on that thread.

Thread 1: Chrome opens → Test A → Test B → Test C → Chrome closes
Thread 2: Chrome opens → Test D → Test E → Test F → Chrome closes

maxActiveSessions still limits the total concurrent browsers.


Writing parallel-safe tests​

Do not use static mutable state — static fields are shared across threads:

// ❌ not thread-safe
public class LoginTest extends BaseTest {
private static LoginPage loginPage; // shared — threads overwrite each other

@Test
public void login() {
loginPage = new LoginPage(getDriver());
loginPage.login("admin", "secret");
}
}
// ✅ thread-safe — instance field, each thread has its own test instance
public class LoginTest extends BaseTest {
private LoginPage loginPage;

@Test
public void login() {
loginPage = new LoginPage(getDriver());
loginPage.login("admin", "secret");
}
}

TestNG creates a new instance of each test class per thread, so instance fields are safe.


Disabling parallel execution​

execution:
parallel: none

Or simply omit execution.parallel — none (sequential execution) is the default.