Laravel Dusk
번역일: 2026년 6월 25일
Laravel Dusk
소개
Laravel Dusk는 직관적이고 사용하기 쉬운 브라우저 자동화 및 테스트 API를 제공합니다. 기본적으로 JDK나 Selenium을 별도로 설치할 필요 없이, 독립 실행형 ChromeDriver만으로 동작합니다. 물론 Selenium 호환 드라이버라면 다른 드라이버를 사용하는 것도 자유롭게 가능합니다.
설치
먼저 Google Chrome을 설치한 뒤, 프로젝트에 laravel/dusk Composer 패키지를 추가합니다:
composer require laravel/dusk --devWARNING
Dusk의 서비스 프로바이더를 직접 등록하는 경우, 절대로 운영 환경에서는 등록하지 마십시오. 운영 환경에서 등록되면 임의의 사용자가 애플리케이션에 인증할 수 있게 되는 보안 취약점이 발생합니다.
패키지 설치 후 dusk:install Artisan 명령을 실행합니다. 이 명령은 tests/Browser 디렉토리와 예제 테스트 파일을 생성하고, 현재 운영체제에 맞는 ChromeDriver 바이너리를 설치합니다:
php artisan dusk:install다음으로 .env 파일의 APP_URL 환경 변수를 설정합니다. 이 값은 브라우저에서 애플리케이션에 접근할 때 사용하는 URL과 동일해야 합니다.
NOTE
Laravel Sail을 사용하여 로컬 개발 환경을 관리하고 있다면, Dusk 테스트 설정 및 실행에 관한 Sail 문서도 함께 참고하세요.
ChromeDriver 버전 관리
dusk:install 명령이 설치하는 ChromeDriver 버전 외에 특정 버전을 직접 설치하려면 dusk:chrome-driver 명령을 사용합니다:
# 현재 OS에 맞는 최신 ChromeDriver 설치php artisan dusk:chrome-driver# 특정 버전 설치php artisan dusk:chrome-driver 86# 지원하는 모든 OS용 특정 버전 설치php artisan dusk:chrome-driver --all# 현재 설치된 Chrome / Chromium 버전에 맞는 ChromeDriver 자동 감지 후 설치php artisan dusk:chrome-driver --detectWARNING
Dusk를 실행하려면 chromedriver 바이너리에 실행 권한이 있어야 합니다. 실행이 되지 않는다면 다음 명령으로 권한을 부여하세요: chmod -R 0755 vendor/laravel/dusk/bin/
다른 브라우저 사용하기
기본적으로 Dusk는 Google Chrome과 ChromeDriver를 사용하지만, 직접 Selenium 서버를 구동하여 원하는 브라우저를 사용할 수도 있습니다.
tests/DuskTestCase.php 파일을 열고, startChromeDriver 호출 부분을 주석 처리하면 Dusk가 ChromeDriver를 자동으로 시작하지 않습니다:
/**
* Dusk 테스트 실행을 준비합니다.
*
* @beforeClass
*/
public static function prepare(): void
{
// static::startChromeDriver();
}그런 다음 driver 메서드를 수정하여 원하는 URL, 포트, WebDriver 설정(desired capabilities)을 지정합니다:
use Facebook\WebDriver\Remote\RemoteWebDriver;
/**
* RemoteWebDriver 인스턴스를 생성합니다.
*/
protected function driver(): RemoteWebDriver
{
return RemoteWebDriver::create(
'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs()
);
}시작하기
테스트 생성
dusk:make Artisan 명령으로 Dusk 테스트를 생성합니다. 생성된 테스트 파일은 tests/Browser 디렉토리에 저장됩니다:
php artisan dusk:make LoginTest각 테스트 후 데이터베이스 초기화
Dusk 테스트는 대부분 데이터베이스에서 데이터를 가져오는 페이지와 상호작용합니다. 그러나 Dusk 테스트에서는 RefreshDatabase 트레이트를 사용하지 마세요. RefreshDatabase는 데이터베이스 트랜잭션을 활용하는데, 이는 HTTP 요청에 걸쳐 동작하지 않습니다. 대신 DatabaseMigrations 또는 DatabaseTruncation 트레이트를 사용하세요.
DatabaseMigrations 사용
DatabaseMigrations 트레이트는 각 테스트 전에 마이그레이션을 실행합니다. 다만 테이블을 매번 삭제하고 다시 생성하기 때문에 DatabaseTruncation에 비해 속도가 느릴 수 있습니다:
Pest
<?php
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
uses(DatabaseMigrations::class);
//PHPUnit
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
//
}WARNING
Dusk 테스트에서는 SQLite 인메모리 데이터베이스를 사용할 수 없습니다. 브라우저는 별도의 프로세스에서 실행되므로, 다른 프로세스의 인메모리 데이터베이스에 접근할 수 없습니다.
DatabaseTruncation 사용
DatabaseTruncation 트레이트는 첫 번째 테스트에서만 마이그레이션을 실행하여 테이블을 생성하고, 이후 테스트에서는 테이블을 TRUNCATE 방식으로 초기화합니다. 매번 마이그레이션을 실행하는 것보다 훨씬 빠릅니다:
Pest
<?php
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Laravel\Dusk\Browser;
uses(DatabaseTruncation::class);
//PHPUnit
<?php
namespace Tests\Browser;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseTruncation;
//
}기본적으로 migrations 테이블을 제외한 모든 테이블이 초기화됩니다. 초기화할 테이블을 직접 지정하려면 테스트 클래스에 $tablesToTruncate 프로퍼티를 정의하세요:
NOTE
Pest를 사용하는 경우, 프로퍼티나 메서드는 기본 DuskTestCase 클래스 또는 테스트 파일이 상속하는 클래스에 정의해야 합니다.
/**
* 초기화할 테이블 목록
*
* @var array
*/
protected $tablesToTruncate = ['users'];반대로 초기화에서 제외할 테이블을 지정하려면 $exceptTables 프로퍼티를 사용합니다:
/**
* 초기화에서 제외할 테이블 목록
*
* @var array
*/
protected $exceptTables = ['users'];초기화할 데이터베이스 커넥션을 지정하려면 $connectionsToTruncate 프로퍼티를 정의합니다:
/**
* 테이블을 초기화할 데이터베이스 커넥션 목록
*
* @var array
*/
protected $connectionsToTruncate = ['mysql'];초기화 전후에 특정 작업을 실행하려면 beforeTruncatingDatabase 또는 afterTruncatingDatabase 메서드를 정의하세요:
/**
* 데이터베이스 초기화 시작 전에 실행할 작업
*/
protected function beforeTruncatingDatabase(): void
{
//
}
/**
* 데이터베이스 초기화 완료 후에 실행할 작업
*/
protected function afterTruncatingDatabase(): void
{
//
}테스트 실행
브라우저 테스트를 실행하려면 dusk Artisan 명령을 사용합니다:
php artisan dusk직전 실행에서 실패한 테스트가 있다면, dusk:fails 명령으로 실패한 테스트만 먼저 재실행하여 시간을 절약할 수 있습니다:
php artisan dusk:failsdusk 명령은 Pest / PHPUnit이 지원하는 인자를 그대로 사용할 수 있습니다. 예를 들어 특정 그룹의 테스트만 실행할 수 있습니다:
php artisan dusk --group=fooNOTE
Laravel Sail을 사용하는 경우, Dusk 테스트 설정 및 실행에 관한 Sail 문서를 참고하세요.
ChromeDriver 수동 시작
Dusk는 기본적으로 ChromeDriver를 자동으로 시작하지만, 환경에 따라 직접 시작해야 할 수도 있습니다. 이 경우 tests/DuskTestCase.php에서 다음 줄을 주석 처리하세요:
/**
* Dusk 테스트 실행을 준비합니다.
*
* @beforeClass
*/
public static function prepare(): void
{
// static::startChromeDriver();
}ChromeDriver를 9515 포트가 아닌 다른 포트로 시작했다면, driver 메서드도 그에 맞게 수정해야 합니다:
use Facebook\WebDriver\Remote\RemoteWebDriver;
/**
* RemoteWebDriver 인스턴스를 생성합니다.
*/
protected function driver(): RemoteWebDriver
{
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()
);
}환경 설정 처리
테스트 실행 시 Dusk 전용 환경 설정 파일을 사용하려면, 프로젝트 루트에 .env.dusk.{environment} 파일을 생성합니다. 예를 들어 local 환경에서 dusk 명령을 실행한다면 .env.dusk.local 파일을 만들면 됩니다.
테스트가 시작되면 Dusk는 기존 .env 파일을 백업하고 Dusk용 환경 파일을 .env로 사용합니다. 테스트가 완료되면 원래 .env 파일이 복원됩니다.
브라우저 기본 사용법
브라우저 인스턴스 생성
로그인 기능을 검증하는 간단한 테스트를 작성해 보겠습니다. browse 메서드에 클로저를 전달하면 Dusk가 브라우저 인스턴스를 자동으로 주입해줍니다:
Pest
<?php
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
uses(DatabaseMigrations::class);
test('기본 예제', function () {
$user = User::factory()->create([
'email' => 'taylor@laravel.com',
]);
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'password')
->press('Login')
->assertPathIs('/home');
});
});PHPUnit
<?php
namespace Tests\Browser;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
/**
* 기본 브라우저 테스트 예제
*/
public function test_basic_example(): void
{
$user = User::factory()->create([
'email' => 'taylor@laravel.com',
]);
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'password')
->press('Login')
->assertPathIs('/home');
});
}
}위 예제에서 볼 수 있듯이, browse 메서드는 클로저를 인자로 받습니다. 클로저에는 Dusk가 브라우저 인스턴스를 자동으로 전달하며, 이 인스턴스를 통해 애플리케이션과의 모든 상호작용과 어설션이 이루어집니다.
여러 브라우저 인스턴스 생성
경우에 따라 테스트를 제대로 수행하려면 여러 브라우저가 필요합니다. 예를 들어 WebSocket과 상호작용하는 채팅 화면을 테스트할 때가 그렇습니다. 이때는 browse 메서드에 전달하는 클로저의 인자를 늘리기만 하면 됩니다:
$this->browse(function (Browser $first, Browser $second) {
$first->loginAs(User::find(1))
->visit('/home')
->waitForText('메시지');
$second->loginAs(User::find(2))
->visit('/home')
->waitForText('메시지')
->type('message', '안녕하세요!')
->press('전송');
$first->waitForText('안녕하세요!')
->assertSee('홍길동');
});페이지 이동
visit 메서드로 특정 URI로 이동할 수 있습니다:
$browser->visit('/login');
이름이 지정된 라우트로 이동하려면 visitRoute 메서드를 사용합니다:
$browser->visitRoute($routeName, $parameters);
back과 forward 메서드로 브라우저 히스토리를 이동할 수 있습니다:
$browser->back();
$browser->forward();페이지를 새로 고침하려면 refresh 메서드를 사용합니다:
$browser->refresh();
브라우저 창 크기 조절
resize 메서드로 브라우저 창 크기를 조절합니다:
$browser->resize(1920, 1080);
maximize 메서드로 브라우저 창을 최대화합니다:
$browser->maximize();
fitContent 메서드는 브라우저 창을 콘텐츠 크기에 맞게 조절합니다:
$browser->fitContent();
테스트 실패 시 Dusk는 스크린샷을 찍기 전에 자동으로 창 크기를 콘텐츠에 맞게 조절합니다. 이 기능을 비활성화하려면 disableFitOnFailure 메서드를 사용하세요:
$browser->disableFitOnFailure();
move 메서드로 브라우저 창의 위치를 이동할 수 있습니다:
$browser->move($x = 100, $y = 100);
브라우저 매크로
여러 테스트에서 반복적으로 사용할 커스텀 브라우저 메서드가 필요하다면 Browser 클래스의 macro 메서드를 활용하세요. 일반적으로 서비스 프로바이더의 boot 메서드에서 정의합니다:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Browser;
class DuskServiceProvider extends ServiceProvider
{
/**
* Dusk 브라우저 매크로를 등록합니다.
*/
public function boot(): void
{
Browser::macro('scrollToElement', function (string $element = null) {
$this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);");
return $this;
});
}
}macro 메서드는 첫 번째 인자로 이름, 두 번째 인자로 클로저를 받습니다. 정의한 매크로는 Browser 인스턴스의 메서드처럼 호출할 수 있습니다:
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/pay')
->scrollToElement('#credit-card-details')
->assertSee('신용카드 정보를 입력하세요');
});인증
인증이 필요한 페이지를 테스트할 때마다 로그인 과정을 반복하면 번거롭습니다. Dusk의 loginAs 메서드를 사용하면 매 테스트마다 로그인 화면을 거치지 않아도 됩니다. 인증 가능한 모델의 기본 키(Primary Key) 또는 모델 인스턴스를 인자로 전달합니다:
use App\Models\User;
use Laravel\Dusk\Browser;
$this->browse(function (Browser $browser) {
$browser->loginAs(User::find(1))
->visit('/home');
});WARNING
loginAs 메서드를 사용하면 해당 파일 내 모든 테스트에 걸쳐 사용자 세션이 유지됩니다.
쿠키
cookie 메서드로 암호화된 쿠키의 값을 읽거나 설정할 수 있습니다. Laravel이 생성하는 쿠키는 기본적으로 모두 암호화됩니다:
$browser->cookie('name');
$browser->cookie('name', 'Taylor');암호화되지 않은 쿠키를 다루려면 plainCookie 메서드를 사용합니다:
$browser->plainCookie('name');
$browser->plainCookie('name', 'Taylor');쿠키를 삭제하려면 deleteCookie 메서드를 사용합니다:
$browser->deleteCookie('name');
JavaScript 실행
script 메서드로 브라우저 내에서 임의의 JavaScript를 실행할 수 있습니다:
$browser->script('document.documentElement.scrollTop = 0');
$browser->script([
'document.body.scrollTop = 0',
'document.documentElement.scrollTop = 0',
]);
$output = $browser->script('return window.location.pathname');스크린샷 찍기
screenshot 메서드로 스크린샷을 찍어 지정한 파일명으로 저장합니다. 모든 스크린샷은 tests/Browser/screenshots 디렉토리에 저장됩니다:
$browser->screenshot('filename');
responsiveScreenshots 메서드는 다양한 중단점(breakpoint)에서 연속으로 스크린샷을 찍습니다:
$browser->responsiveScreenshots('filename');
screenshotElement 메서드로 페이지의 특정 요소만 스크린샷을 찍을 수 있습니다:
$browser->screenshotElement('#selector', 'filename');
콘솔 출력 저장
storeConsoleLog 메서드로 브라우저의 콘솔 출력을 파일로 저장합니다. 저장 위치는 tests/Browser/console 디렉토리입니다:
$browser->storeConsoleLog('filename');
페이지 소스 저장
storeSource 메서드로 현재 페이지의 HTML 소스를 파일로 저장합니다. 저장 위치는 tests/Browser/source 디렉토리입니다:
$browser->storeSource('filename');
요소 조작
Dusk 셀렉터
Dusk 테스트에서 가장 까다로운 부분 중 하나는 요소를 선택할 때 사용할 CSS 셀렉터를 관리하는 일입니다. 프론트엔드 코드가 변경되면 아래와 같은 CSS 셀렉터가 쉽게 깨질 수 있습니다:
// HTML...
<button>로그인</button>
// 테스트...
$browser->click('.login-page .container div > button');Dusk 셀렉터를 사용하면 CSS 셀렉터 대신 의미 있는 식별자로 요소를 선택할 수 있습니다. HTML 요소에 dusk 속성을 추가한 뒤, 테스트에서 @ 접두사를 붙여 사용하세요:
// HTML...
<button dusk="login-button">로그인</button>
// 테스트...
$browser->click('@login-button');필요하다면 Dusk 셀렉터가 사용하는 HTML 속성을 selectorHtmlAttribute 메서드로 변경할 수 있습니다. 일반적으로 AppServiceProvider의 boot 메서드에서 호출합니다:
use Laravel\Dusk\Dusk;
Dusk::selectorHtmlAttribute('data-dusk');텍스트, 값, 속성
값 읽기 및 설정
CSS 또는 Dusk 셀렉터에 해당하는 요소의 값을 읽거나 설정하려면 value 메서드를 사용합니다:
// 값 읽기
$value = $browser->value('selector');
// 값 설정
$browser->value('selector', 'value');특정 이름을