laravel/dusk
Laravel Dusk is Laravel’s browser automation and end-to-end testing tool, offering a clean, expressive API for driving real browsers. Runs with a bundled standalone Chromedriver by default (no Selenium/JDK required), but supports other drivers too.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require --dev laravel/dusk
laravel/dusk:install
This installs Chromedriver (standalone) and configures PHPUnit.
First Test:
Create a test in tests/Browser/ExampleTest.php:
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
/** @test */
public function it_loads_the_homepage()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertSee('Welcome');
});
}
}
Run with:
php artisan dusk
Key Files:
tests/Browser/ – Default test directory.dusk.php – Configuration file (e.g., browser drivers, timeouts).phpunit.xml – PHPUnit configuration (includes Dusk-specific settings).Browser Interaction: Use fluent methods for common actions:
$browser->visit('/dashboard')
->click('@login-button')
->type('@email', 'user@example.com')
->press('@submit')
->assertPathIs('/dashboard');
@id, .class, #element, or custom CSS selectors.where():
$browser->click('@submit')->where('disabled', false);
Page Objects:
Encapsulate complex pages in Page classes (auto-generated via php artisan dusk:page):
namespace Tests\Browser\Pages;
use Laravel\Dusk\Page;
class DashboardPage extends Page
{
public function assertSeeUser($name)
{
return $this->assertSee($name);
}
}
Usage:
$this->browse(function (Browser $browser) {
$browser->visit('/dashboard')
->assertSeeUser('John Doe');
});
Component Testing:
Test Vue/React components with assertVue():
$browser->assertVue('welcome', [
'message' => 'Hello, Dusk!',
]);
Data-Driven Tests: Use PHPUnit data providers:
public function providerTestData()
{
return [
['user1@example.com', 'User 1'],
['user2@example.com', 'User 2'],
];
}
/** @dataProvider providerTestData */
public function test_login($email, $name)
{
$this->browse(function (Browser $browser) use ($email, $name) {
$browser->visit('/login')
->type('@email', $email)
->press('@submit')
->assertSee($name);
});
}
Debugging Tools:
$browser->screenshot('login-page');$browser->dumpConsoleLogs();$browser->elementScreenshot('@login-button');Custom Assertions:
Extend MakesAssertions or create helper methods:
$browser->assertElementVisible('@submit')
->assertAttributeContains('@input', 'value', 'test');
CI/CD:
DUSK_DRIVER=chrome to specify the browser.- name: Cache Chromedriver
uses: actions/cache@v3
with:
path: ~/.cache/chromedriver
key: ${{ runner.os }}-chromedriver
Headless Mode:
Configure in dusk.php:
'headless' => [
'chrome' => true,
'firefox' => false,
],
Or override per test:
$this->browse(function (Browser $browser) {
$browser->driver->setHeadless(true);
// ...
});
Parallel Testing:
Use PHPUnit’s --parallel flag:
php artisan dusk --parallel
Database Transactions:
Leverage Laravel’s transactions in DuskTestCase:
protected function setUp(): void
{
parent::setUp();
$this->artisan('migrate:fresh');
}
Custom Drivers:
Extend ChromeDriver or FirefoxDriver for custom logic:
class CustomDriver extends ChromeDriver
{
public function customMethod()
{
// ...
}
}
Register in dusk.php:
'drivers' => [
'custom' => [
'class' => \Tests\Browser\CustomDriver::class,
],
],
Selector Ambiguity:
.btn). Use IDs or data attributes:
<button data-testid="submit-btn">Submit</button>
$browser->click('@submit-btn');
Flaky Tests:
$browser->waitFor(5)->assertSee('Content');
waitForTextIn() for dynamic content:
$browser->waitForTextIn('@element', 'Expected Text', 10);
dusk.php:
'chrome' => [
'args' => ['--disable-gpu', '--no-sandbox', '--disable-dev-shm-usage'],
],
Chromedriver Version Mismatch:
composer require --dev laravel/dusk --dev
php artisan dusk:chrome-driver
Slow Tests:
->withoutMiddleware() for API-heavy tests:
$this->browse(function (Browser $browser) {
$browser->withoutMiddleware()->visit('/api/endpoint');
});
Environment Issues:
.env exists in the project root.APP_ENV=testing and DB_CONNECTION=sqlite for isolated tests.Assertion Failures:
dd($browser->html()) to inspect the DOM.$browser->assertVisible('@element');
Reusable Test Logic:
Create helper methods in DuskTestCase:
protected function loginAsUser($email, $password)
{
$this->browse(function (Browser $browser) use ($email, $password) {
$browser->visit('/login')
->type('@email', $email)
->type('@password', $password)
->press('@submit');
});
}
Visual Regression Testing:
Use elementScreenshot() to compare UI states:
$browser->elementScreenshot('@header', 'header-before-login');
Custom Commands:
Extend Browser for domain-specific actions:
namespace Tests\Browser;
use Laravel\Dusk\Browser;
class CustomBrowser extends Browser
{
public function fillForm(array $data)
{
foreach ($data as $field => $value) {
$this->type("@{$field}", $value);
}
return $this;
}
}
Usage:
$this->browse(function (CustomBrowser $browser) {
$browser->fillForm(['email' => 'test@example.com', 'name' => 'Test']);
});
Performance Profiling: Measure test execution time:
$start = microtime(true);
$this->browse(function (Browser $browser) { /* ... */ });
$time = microtime(true) - $start;
$this->assertLessThan(2, $time, 'Test took too long');
Cross-Browser Testing:
Configure multiple drivers in dusk.php:
'drivers' => [
'chrome' => [
'path' => '/path/to/chromedriver',
],
'firefox' => [
'path' => '/path/to/geckodriver',
],
],
Run specific driver:
DUSK_DRIVER
How can I help you explore Laravel packages today?