Laravel Dusk drives a real Chrome browser. Use it when a feature test cannot cover the behaviour: JavaScript, Livewire or Alpine components, modals and drag and drop.

Setup

bash
composer require laravel/dusk --dev
php artisan dusk:install
php artisan dusk

dusk:install creates tests/Browser and tests/DuskTestCase.php. Keep a separate .env.dusk.local with its own database and APP_URL; Dusk swaps it in while the tests run.

How Dusk works
How Dusk works

💡 php artisan dusk --filter=LoginTest runs one class and php artisan dusk:fails reruns only the failed tests.

The first test

php
namespace Tests\Browser;

use Laravel\Dusk\Browser;
use Tests\DuskTestCase;

class LoginTest extends DuskTestCase
{
    public function test_user_can_log_in(): void
    {
        $this->browse(function (Browser $browser) {
            $browser->visit('/login')
                ->type('email', 'taylor@example.com')
                ->type('password', 'secret')
                ->press('Log in')
                ->assertPathIs('/dashboard');
        });
    }
}

browse() opens a browser and passes a Browser object. Every call returns the same object, so the steps read like a story. When a test fails, Dusk saves a screenshot to tests/Browser/screenshots and the console log to tests/Browser/console.

Selectors

Prefer a dusk attribute over CSS classes. @save-button selects [dusk="save-button"].

php
// <button dusk="save-button" class="btn btn-primary">Save</button>

$browser->click('@save-button')
    ->assertSeeIn('@toast', 'Saved');