Testing JavaScript interactions requires a real browser. Laravel Dusk provides elegant browser automation with Chrome, making end-to-end testing straightforward. At ZIRA Software, Dusk catches UI bugs that unit tests miss, ensuring our applications work perfectly for real users.
Why Laravel Dusk?
Browser testing needs:
- Test JavaScript interactions
- Verify UI behavior
- Test form submissions
- Check dynamic content
- Validate workflows
- Catch integration bugs
Dusk provides:
- Chrome automation
- Elegant API
- Screenshot capture
- Component testing
- User simulation
- Continuous integration support
Installation
composer require --dev laravel/dusk
php artisan dusk:install
Creates:
tests/Browserdirectorytests/DuskTestCase.php.env.dusk.localfor test environment
First Test
tests/Browser/ExampleTest.php:
<?php
namespace Tests\Browser;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
/** @test */
public function basic_example()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertSee('Laravel');
});
}
}
Run tests:
php artisan dusk
Browser Interactions
Navigation:
$browser->visit('/login')
->visitRoute('profile', ['id' => 1])
->back()
->forward()
->refresh();
Clicking:
$browser->click('.button')
->clickLink('About')
->clickAtXPath('//button[@type="submit"]')
->clickAtPoint(100, 200);
Forms:
$browser->type('email', 'taylor@laravel.com')
->select('size', 'Large')
->check('terms')
->radio('color', 'red')
->attach('photo', '/path/to/photo.jpg')
->press('Submit');
Assertions:
$browser->assertTitle('Welcome')
->assertTitleContains('Laravel')
->assertSee('Hello World')
->assertDontSee('Error')
->assertPathIs('/home')
->assertRouteIs('profile', ['id' => 1]);
Login Testing
/** @test */
public function user_can_login()
{
$user = factory(User::class)->create([
'email' => 'taylor@laravel.com',
'password' => bcrypt('password'),
]);
$this->browse(function (Browser $browser) {
$browser->visit('/login')
->type('email', 'taylor@laravel.com')
->type('password', 'password')
->press('Login')
->assertPathIs('/dashboard')
->assertSee('Welcome back');
});
}
Login helper:
public function test_dashboard_requires_authentication()
{
$this->browse(function (Browser $browser) {
$browser->loginAs(User::find(1))
->visit('/dashboard')
->assertSee('Dashboard');
});
}
JavaScript Execution
Execute JavaScript:
$browser->script('window.scrollTo(0, 500)');
$output = $browser->script('return document.title');
Wait for JavaScript:
$browser->waitFor('.selector')
->waitForText('The Message')
->waitUntilMissing('.loading')
->waitForLocation('/profile')
->whenAvailable('.modal', function ($modal) {
$modal->assertSee('Modal Content')
->press('OK');
});
Pages
Create page object:
php artisan dusk:page Login
tests/Browser/Pages/Login.php:
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
class Login extends Page
{
public function url()
{
return '/login';
}
public function assert(Browser $browser)
{
$browser->assertPathIs($this->url());
}
public function elements()
{
return [
'@email' => 'input[name="email"]',
'@password' => 'input[name="password"]',
'@submit' => 'button[type="submit"]',
];
}
public function login(Browser $browser, $email, $password)
{
$browser->type('@email', $email)
->type('@password', $password)
->click('@submit');
}
}
Use page object:
use Tests\Browser\Pages\Login;
$this->browse(function (Browser $browser) {
$browser->visit(new Login)
->login('taylor@laravel.com', 'password')
->assertPathIs('/dashboard');
});
Components
Create component:
php artisan dusk:component DatePicker
tests/Browser/Components/DatePicker.php:
<?php
namespace Tests\Browser\Components;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Component as BaseComponent;
class DatePicker extends BaseComponent
{
public function selector()
{
return '.date-picker';
}
public function assert(Browser $browser)
{
$browser->assertVisible($this->selector());
}
public function selectDate(Browser $browser, $year, $month, $day)
{
$browser->select('@year', $year)
->select('@month', $month)
->select('@day', $day);
}
public function elements()
{
return [
'@year' => 'select.year',
'@month' => 'select.month',
'@day' => 'select.day',
];
}
}
Use component:
use Tests\Browser\Components\DatePicker;
$this->browse(function (Browser $browser) {
$browser->visit('/profile')
->within(new DatePicker, function ($browser) {
$browser->selectDate('2017', '01', '15');
});
});
Screenshots
On failure:
// Automatic in DuskTestCase
protected function tearDown(): void
{
foreach (static::$browsers as $browser) {
$browser->screenshot('failure');
}
parent::tearDown();
}
Manual:
$browser->screenshot('my-screenshot');
Stored in: tests/Browser/screenshots/
Continuous Integration
GitHub Actions:
name: Dusk Tests
on: [push]
jobs:
dusk:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:5.7
env:
MYSQL_DATABASE: testing
MYSQL_ROOT_PASSWORD: password
ports:
- 3306:3306
steps:
- uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
- name: Install dependencies
run: composer install
- name: Prepare environment
run: |
cp .env.example .env
php artisan key:generate
- name: Run migrations
run: php artisan migrate
env:
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_DATABASE: testing
DB_USERNAME: root
DB_PASSWORD: password
- name: Start Chrome Driver
run: ./vendor/laravel/dusk/bin/chromedriver-linux &
- name: Run Laravel Server
run: php artisan serve &
- name: Run Dusk Tests
run: php artisan dusk
- name: Upload Screenshots
if: failure()
uses: actions/upload-artifact@v2
with:
name: screenshots
path: tests/Browser/screenshots
Best Practices
- Use page objects - Reusable page logic
- Create components - Reusable UI components
- Add waits - For dynamic content
- Take screenshots - On failures
- Run in CI - Catch regressions
- Test critical paths - User workflows
- Keep tests fast - Parallelize if possible
Conclusion
Laravel Dusk makes browser testing accessible and enjoyable. From login forms to complex JavaScript interactions, Dusk catches bugs unit tests miss. At ZIRA Software, Dusk is essential for ensuring flawless user experiences.
Start with critical user flows. Build page objects for reusability. Run in CI to catch regressions early.
Need comprehensive testing for your Laravel application? Contact ZIRA Software to discuss browser automation, end-to-end testing strategies, and quality assurance.