Run your Laravel Dusk browser tests in parallel — multiple ChromeDriver instances, one CI runner, faster feedback.
This package is currently in beta. It may not work, so give it a try and create an issue if something is broken - a failing test would be fantastic!
Dusk tests are slow by nature: each test launches a real browser, navigates to a page, and waits for it to render. A suite of 20–30 tests can easily take several minutes when run sequentially.
The common workaround is splitting tests across multiple CI runners — but that means paying for extra parallel jobs. This package takes a different approach: it runs multiple ChromeDriver instances on the same runner, splitting your test suite across them automatically. You get the speed of parallelism without the added infrastructure cost.
- PHP 8.1+
- Laravel 10+
- Laravel Dusk 8+
- ParaTest (
brianium/paratest)
composer require --dev jackbayliss/laravel-dusk-parallel
composer require --dev brianium/paratestYou need one ChromeDriver instance per parallel process. By default the package assigns ports starting from 9515. Start a few more than you intend to use, as ParaTest's token assignment can vary by environment:
chromedriver --port=9515 &
chromedriver --port=9516 &
chromedriver --port=9517 &
chromedriver --port=9518 &php artisan dusk:parallelBy default this uses 2 parallel processes. Pass --processes to change it:
php artisan dusk:parallel --processes=4If port 9515 is already in use, set DUSK_DRIVER_BASE_PORT in your .env:
DUSK_DRIVER_BASE_PORT=9600Worker processes will use ports 9600, 9601, etc. Remember to start ChromeDriver on those ports.
For Selenium Grid, BrowserStack, or any other remote WebDriver, set DUSK_DRIVER_URL and the port logic is bypassed entirely:
DUSK_DRIVER_URL=http://selenium-grid:4444Extend the package's TestCase in your tests/DuskTestCase.php and override the driver() method:
use JackBayliss\DuskParallel\ParallelDriver;
use JackBayliss\DuskParallel\TestCase as ParallelTestCase;
abstract class DuskTestCase extends ParallelTestCase
{
protected function driver(): RemoteWebDriver
{
$options = (new ChromeOptions)->addArguments([
'--headless=new',
'--no-sandbox',
'--disable-dev-shm-usage',
]);
return RemoteWebDriver::create(
ParallelDriver::resolveDriverUrl(),
DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY,
$options
)
);
}
}Extending the package's
TestCaseis optional — it works with a standard Laravel Dusk setup out of the box.
- name: Start ChromeDriver instances
run: |
chromedriver --port=9515 &
chromedriver --port=9516 &
chromedriver --port=9517 &
chromedriver --port=9518 &
sleep 2
- name: Run Dusk tests
run: php artisan dusk:parallel --processes=2All ChromeDriver instances run on the same runner, so there is no additional CI cost compared to running Dusk sequentially.
ParaTest splits your test suite across multiple worker processes. Each worker receives a TEST_TOKEN environment variable (0, 1, 2 …) which the package uses to:
- Route that worker's ChromeDriver to a unique port (
basePort + TEST_TOKEN). - Set a
dusk_db_tokencookie on the browser so every HTTP request is handled by the correct per-worker test database.
Each worker gets a fully independent browser session and database — all within a single CI job.
| Script | Description |
|---|---|
composer test |
Run the test suite |
composer lint |
Fix code style with Pint |
composer analyse |
Static analysis with PHPStan |
See dusk-parallel-demo for a working Laravel application with passing parallel Dusk tests and a complete GitHub Actions workflow.
MIT