laravel/dusk
Laravel Dusk is Laravel’s browser automation and testing tool, offering an expressive API for end-to-end tests. It runs with a bundled standalone ChromeDriver by default (no Selenium or JDK required), but can use other Selenium drivers if needed.
Installation:
composer require --dev laravel/dusk
Run the Dusk installer:
php artisan dusk:install
This installs Chromedriver, configures PHPUnit, and sets up the base test class.
First Test:
Create a test in tests/Browser/ExampleTest.php:
<?php
namespace Tests\Browser;
use Laravel\Dusk\TestCase;
class ExampleTest extends TestCase
{
/** @test */
public function it_loads_the_homepage()
{
$this->visit('/')
->assertSee('Welcome');
}
}
Run the test:
php artisan dusk
Key Files:
tests/Browser/ – Default directory for Dusk tests.phpunit.xml – Configured for Dusk (includes Selenium setup).DuskTestCase.php – Base test class (auto-generated by installer).Basic Interaction:
$this->visit('/dashboard')
->type('email', 'user@example.com')
->press('Login')
->assertPathIs('/dashboard');
Form Testing:
$this->visit('/register')
->type('name', 'John Doe')
->select('country', 'United States')
->check('terms')
->press('Register')
->assertSee('Account created!');
Dynamic Assertions:
$this->visit('/posts')
->assertSeeIn('h1', 'Latest Posts')
->assertPathContains('/posts')
->assertAttributeMissing('input[name="hidden"]', 'value');
Component Testing:
$this->component('Alert', ['message' => 'Success!'])
->assertSee('Success!');
Page Objects:
Define reusable page interactions in tests/Browser/Pages/:
// tests/Browser/Pages/DashboardPage.php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Page;
class DashboardPage extends Page
{
public function assertSeeUser($name)
{
return $this->assertSee($name);
}
}
Use in tests:
$this->visit('/dashboard')
->assertSeeUser('John Doe');
Screenshots & Debugging:
$this->visit('/error-page')
->screenshot('error-page')
->dump(); // Dumps browser state
actingAs() for auth:
$this->actingAs(User::first())
->visit('/profile');
$response = $this->post('/login', ['email' => 'test@example.com']);
$this->visit('/dashboard')->assertSee('Welcome');
php artisan dusk --headless
DUSK_DRIVER_URL in .env:
DUSK_DRIVER_URL=remote:http://selenium-standalone-chrome:4444/wd/hub
Element Selection:
->click('#submit-button') over ->click('button', 'Submit')).->waitFor() for dynamic content:
$this->waitFor(5)->assertSee('Loaded');
Flaky Tests:
$this->pause(2000); // 2-second pause
->clickOnce() (or ->clickWhen()) to avoid double-clicks:
$this->clickOnce('#submit')->assertPathIs('/success');
Configuration Quirks:
vendor/laravel/dusk/bin/chromedriver.DUSK_DRIVER_PORT=9515 if using custom drivers.--headless=new (Chrome 112+) or --headless=old for compatibility.Debugging:
phpunit.xml:
<env name="DUSK_SCREENSHOTS" value="1"/>
storage/logs/dusk-*.log for Selenium errors.->dump() or ->dd() to inspect the browser state.Performance:
--parallel in PHPUnit for faster runs:
php artisan dusk --parallel
@slow and filter them out in CI:
php artisan dusk --exclude-group=slow
Custom Assertions:
Extend Laravel\Dusk\Browser in a trait:
trait CustomAssertions
{
public function assertElementIsVisible($selector)
{
return $this->assertTrue($this->element($selector)->isVisible());
}
}
Use in tests:
$this->assertElementIsVisible('#modal');
Custom Selectors:
Override find() in a subclass:
class CustomBrowser extends Browser
{
public function findCustomSelector($selector)
{
return $this->driver->findElement(WebDriverBy::css($selector));
}
}
Pest Integration:
Use uses(DuskTestCase::class) in Pest tests:
it('loads the homepage', function () {
$this->visit('/')->assertSee('Welcome');
})->uses(DuskTestCase::class);
CI Optimization:
docker run --rm -d -p 9515:9515 selenium/standalone-chrome
DUSK_DRIVER=remote in .env to connect to a shared Selenium grid.Vue/React Testing:
Use assertVue() for SPAs:
$this->assertVue('data().message', 'Welcome');
Or assertJavascript() for custom JS checks:
$this->assertJavascript('document.title === "Home"');
How can I help you explore Laravel packages today?