Dusk: browser testing automatizado no Laravel

1. Introdução ao Laravel Dusk e seu Propósito

Laravel Dusk é uma ferramenta de teste de browser expressiva e fácil de usar, desenvolvida especificamente para o ecossistema Laravel. Enquanto testes unitários verificam métodos individuais e testes de integração validam interações entre componentes, o Dusk permite simular ações reais de usuários em um navegador completo.

Diferente do PHPUnit Headless, que executa testes sem interface gráfica e sem suporte a JavaScript, o Dusk opera sobre um navegador real (Chrome) controlado via ChromeDriver ou Selenium. Isso significa que ele consegue testar componentes Vue.js, React, Livewire e qualquer interação JavaScript que sua aplicação execute.

A grande vantagem do Dusk sobre ferramentas como Selenium puro é sua integração nativa com o Laravel. Você não precisa configurar WebDriver manualmente nem gerenciar sessões complexas — o framework já oferece helpers prontos para autenticação, manipulação de cookies e interação com elementos DOM.

2. Instalação e Configuração Inicial

Para instalar o Dusk, utilize o Composer:

composer require --dev laravel/dusk

Em seguida, registre o service provider e instale os arquivos base:

php artisan dusk:install

Este comando cria o diretório tests/Browser/ e baixa a versão compatível do ChromeDriver:

php artisan dusk:chrome-driver

Para ambientes de teste, crie arquivos .env.dusk.local ou .env.dusk.testing com variáveis específicas:

APP_URL=http://localhost:8000
DB_DATABASE=testing_dusk

Você pode escolher entre executar com ChromeDriver (padrão) ou Selenium. Para modo headless (útil em servidores CI), configure no arquivo tests/DuskTestCase.php:

protected function driver()
{
    $options = (new ChromeOptions)->addArguments([
        '--disable-gpu',
        '--headless',
        '--window-size=1920,1080',
    ]);

    return RemoteWebDriver::create(
        'http://localhost:9515', DesiredCapabilities::chrome()->setCapability(
            ChromeOptions::CAPABILITY, $options
        )
    );
}

3. Estrutura e Criação de Testes com Dusk

Os testes ficam organizados em tests/Browser/. Uma classe básica estende DuskTestCase:

<?php

namespace Tests\Browser;

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

class LoginTest extends DuskTestCase
{
    public function test_usuario_pode_fazer_login()
    {
        $this->browse(function (Browser $browser) {
            $browser->visit('/login')
                    ->type('email', 'usuario@exemplo.com')
                    ->type('password', 'senha123')
                    ->click('button[type="submit"]')
                    ->assertPathIs('/dashboard')
                    ->assertSee('Bem-vindo');
        });
    }
}

Métodos fundamentais incluem:

  • visit() — navega para uma URL
  • type() — preenche campos de formulário
  • click() — clica em elementos
  • assertSee() — verifica texto visível na página
  • with() — foca em um elemento específico (ex.: $browser->with('.modal')->assertSee('Confirmar'))
  • elsewhere() — interage com elementos fora do escopo atual

4. Interações Avançadas com o Navegador

Para upload de arquivos:

$browser->attach('foto', __DIR__.'/files/teste.jpg')
        ->press('Enviar')
        ->assertSee('Upload realizado');

Trabalhando com JavaScript:

$browser->script('document.getElementById("meu-botao").click()');
$browser->waitFor('.carregando', 5)  // espera até 5 segundos
        ->waitUntil('document.readyState === "complete"')
        ->pause(1000);  // pausa de 1 segundo

Navegação entre páginas:

$browser->clickLink('Próxima página')
        ->back()
        ->forward()
        ->refresh();

Para debug, capture screenshots e logs:

$browser->screenshot('falha-login')
        ->storeConsoleLog('console-errors');

5. Page Objects e Componentes Reutilizáveis

Page Objects organizam seletores e ações de páginas específicas. Crie uma classe em tests/Browser/Pages/:

<?php

namespace Tests\Browser\Pages;

use Laravel\Dusk\Browser;
use Laravel\Dusk\Page;

class LoginPage extends Page
{
    public function url()
    {
        return '/login';
    }

    public function assert(Browser $browser)
    {
        $browser->assertSee('Entrar');
    }

    public function elements()
    {
        return [
            '@email' => 'input[name="email"]',
            '@senha' => 'input[name="password"]',
            '@botao' => 'button[type="submit"]',
        ];
    }

    public function fazerLogin(Browser $browser, $email, $senha)
    {
        $browser->type('@email', $email)
                ->type('@senha', $senha)
                ->click('@botao');
    }
}

Componentes são ideais para blocos de UI repetitivos, como modais:

<?php

namespace Tests\Browser\Components;

use Laravel\Dusk\Component;

class Modal extends Component
{
    public function selector()
    {
        return '.modal';
    }

    public function assert(Browser $browser)
    {
        $browser->assertVisible('@fechar');
    }

    public function elements()
    {
        return [
            '@fechar' => '.modal-close',
            '@titulo' => '.modal-title',
        ];
    }
}

6. Testes de Autenticação e Sessão

Para simular login, utilize loginAs():

public function test_usuario_autenticado()
{
    $usuario = User::factory()->create();

    $this->browse(function (Browser $browser) use ($usuario) {
        $browser->loginAs($usuario)
                ->visit('/dashboard')
                ->assertSee($usuario->name);
    });
}

Testando fluxos de registro:

$browser->visit('/register')
        ->type('name', 'João Silva')
        ->type('email', 'joao@exemplo.com')
        ->type('password', 'senha123')
        ->type('password_confirmation', 'senha123')
        ->click('button[type="submit"]')
        ->assertAuthenticated();

Manipulação de cookies:

$browser->cookie('meu_cookie', 'valor')
        ->withCookie('outro_cookie', 'outro_valor')
        ->visit('/cookies');

7. Estratégias para Dados de Teste e Ambiente

Use DatabaseMigrations para garantir um estado limpo:

use Illuminate\Foundation\Testing\DatabaseMigrations;

class ProdutoTest extends DuskTestCase
{
    use DatabaseMigrations;

    public function test_criacao_produto()
    {
        $categoria = Categoria::factory()->create();

        $this->browse(function (Browser $browser) use ($categoria) {
            $browser->visit('/produtos/criar')
                    ->type('nome', 'Produto Teste')
                    ->select('categoria_id', $categoria->id)
                    ->press('Salvar')
                    ->assertSee('Produto criado com sucesso');
        });
    }
}

Para paralelismo, configure bancos separados no .env.dusk.testing:

DB_DATABASE=testing_dusk_{process_id}

Evite efeitos colaterais usando afterClass para limpeza:

protected static function afterClass()
{
    parent::afterClass();
    // limpar arquivos temporários
}

8. Integração Contínua e Boas Práticas

No GitHub Actions, configure o workflow:

- name: Run Dusk Tests
  run: |
    php artisan serve --env=dusk.testing &
    php artisan dusk --parallel

Para execução headless, garanta que o ChromeDriver esteja instalado e configure as opções headless no DuskTestCase.

Dicas de performance:

  • Use --parallel para executar testes em paralelo
  • Fragmente suites grandes em grupos menores
  • Ajuste timeouts com $browser->waitFor('.elemento', 10)

Tratamento de falhas:

public function test_com_retry()
{
    $this->browse(function (Browser $browser) {
        $browser->waitForText('Sucesso', 10)
                ->retry(3, function ($browser) {
                    $browser->click('.confirmar');
                });
    });
}

Referências