Running tests in parallel
Run mobile tests at once, keep the results together, and understand why devices queue more than browsers.
This works the same way as it does on desktop. Your test runner opens several sessions, and each one gets its own device.
What differs is supply. We own a fixed number of phones, and each serves one session at a time.
1. Turn on workers
TestNG, in testng.xml:
<suite name="mobile" parallel="methods" thread-count="3">
<test name="signin">
<classes>
<class name="tests.SignInTest"/>
</classes>
</test>
</suite>Mocha:
npx mocha --parallel --jobs 3 "test/**/*.spec.js"pytest, with pytest-xdist:
pytest -n 3Give every worker its own driver. One driver shared between threads will not survive.
2. Give every worker the same runId
Three workers means three sessions. Without a runId that is three separate runs in your dashboard:
| Language | The line |
|---|---|
| Java | tk.put("runId", System.getenv("GITHUB_RUN_ID")); |
| JavaScript | runId: process.env.GITHUB_RUN_ID |
| Python | "runId": os.environ["GITHUB_RUN_ID"] |
build does not group anything. It is a label. Sessions that share a build but no runId are still separate runs.3. How many at once
| Limit | |
|---|---|
| Paid plans | One session per thread, 1 to 20 threads |
| Free tier | No thread limit — your remaining free minutes are the only ceiling |
| Over your limit | Waits for one of your threads to free up, then refuses if none does |
Going over your limit no longer fails straight away. The session waits for one of your own threads, and only gives up if none comes free in time:
Waited, but all 3 of your parallel sessions were busy the whole time.
Close one, or raise your plan's parallel limit.How long it waits is the same maxQueueWaitSeconds below, and the two waits
share it: time spent waiting for a thread is time not left for waiting for a
device.
4. Why devices queue more than browsers
A desktop machine can hold two browser sessions. A phone holds one.
So the same thread count goes less far here, and what you ask for matters more.
Naming a model narrows you to that model. Asking for an iPhone 16 waits for an iPhone 16.
Leave the name out and any free iPhone will do. That is often the difference between starting now and waiting five minutes.
Virtual devices queue far less. There are more of them and they start faster.
If a test does not need real hardware, ask for a simulator or emulator. Keep the real phones for the tests that need them.
Cleaning takes time. A device is wiped between sessions, so it is not free the instant your test ends.
Run twice in a row against one named model and the second waits for the first.
Waiting is set by maxQueueWaitSeconds — five minutes by default, up to 9 minutes — and it is not billed.
A sensible shape for CI
Run most of your tests on virtual devices, with no model named, at your full thread count.
Run a smaller set on named real phones, with fewer workers and a longer maxQueueWaitSeconds.
The full picture — thread bands, free minutes — is in Reference.
Network capture
Get a real HAR file of everything a phone sent during your test, and know when it did not work.
Espresso on real devices
Upload the two APKs Gradle already builds — your app APK and its androidTest APK — and run your Espresso tests on our real Android phones and emulators, from CI, with no change to your tests.
