본문 바로가기

Laravel Dusk

업데이트됨

번역일: 2026년 9월 17일

이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.

원문 수정
2026년 9월 17일
번역 갱신
2026년 9월 17일

Laravel Dusk

소개

Laravel Dusk는 사용하기 쉽고 직관적인 브라우저 자동화 및 테스트 API를 제공합니다. 기본적으로 Dusk는 로컬 컴퓨터에 JDK나 Selenium 같은 별도 프로그램을 설치하지 않아도 됩니다. 대신 Dusk는 독립 실행형 ChromeDriver 설치본을 함께 사용합니다. 다만 원한다면 다른 Selenium 호환 드라이버를 자유롭게 사용할 수도 있습니다.

NOTE

회원가입 폼 작성, 로그인, 결제 과정 등 실제 브라우저에서 사용자가 클릭하고 입력하는 흐름을 그대로 재현해 테스트하고 싶다면 Dusk가 적합합니다. 단순히 HTTP 응답이나 JSON 구조를 검증하는 수준이라면 더 가벼운 HTTP 테스트만으로 충분할 수 있습니다.

설치

먼저 프로젝트에 Google Chrome 브라우저를 설치하고, laravel/dusk Composer 의존성을 프로젝트에 추가해야 합니다.

composer require laravel/dusk --dev

WARNING

Dusk의 서비스 프로바이더를 프로덕션 환경에서 직접 등록하고 있다면, 등록되지 않은 사용자가 Dusk의 라우트에 접근할 수 있게 될 위험이 있으므로 반드시 APP_ENV 환경 변수가 production이 아닌지 확인해야 합니다. 이런 위험을 방지하려면 local 환경에서만 프로바이더가 등록되도록 다음과 같이 조건부로 등록하세요.

/** * Register any application services. */ public function register(): void { if ($this->app->environment('local', 'testing')) { $this->app->register(\Laravel\Dusk\DuskServiceProvider::class); } }

Composer 패키지를 설치했다면, dusk:install Artisan 명령어를 실행하세요. 이 명령어는 tests/Browser 디렉터리와 예제 Dusk 테스트, 그리고 사용 중인 운영체제에 맞는 ChromeDriver 바이너리를 생성합니다.

php artisan dusk:install

이어서 애플리케이션의 .env 파일에 APP_URL 환경 변수를 설정하세요. 이 값은 브라우저에서 애플리케이션에 접속할 때 사용하는 URL과 일치해야 합니다.

NOTE

Laravel Sail로 로컬 개발 환경을 관리하고 있다면 Dusk 테스트 설정 및 실행에 관한 Sail 문서도 함께 참고하세요.

ChromeDriver 설치 관리하기

dusk:install 명령어로 설치되는 기본 ChromeDriver와 다른 버전을 설치하고 싶다면 dusk:chrome-driver 명령어를 사용할 수 있습니다.

# 운영체제에 맞는 최신 버전 설치...php artisan dusk:chrome-driver# 운영체제에 맞는 특정 버전 설치...php artisan dusk:chrome-driver 86# 지원되는 모든 운영체제용 특정 버전 설치...php artisan dusk:chrome-driver --all# 사용 중인 Chrome/Chromium 버전에 맞는 ChromeDriver 버전 자동 감지 후 설치...php artisan dusk:chrome-driver --detect

WARNING

Dusk가 요구하는 실행 권한(chromedriver 바이너리 실행 권한 등)이 정상적으로 부여되도록, dusk:chrome-driver 명령어를 실행하기 전에 chmod +x 등을 통해 바이너리 실행 권한 문제가 없는지 확인하세요. 만약 실행 권한 문제가 발생한다면 다음 명령어로 ChromeDriver 바이너리를 실행 가능하도록 설정할 수 있습니다: chmod -R 0755 vendor/laravel/dusk/bin/

다른 브라우저 사용하기

기본적으로 Dusk는 Google Chrome과 독립 실행형 ChromeDriver 설치본을 사용해 브라우저 테스트를 실행합니다. 하지만 원하는 Selenium 서버를 직접 띄우고, 원하는 어떤 브라우저에 대해서도 테스트를 실행할 수 있습니다.

시작하려면 애플리케이션의 기본 Dusk 테스트 케이스인 tests/DuskTestCase.php 파일을 열어 startChromeDriver 메서드 호출 부분을 제거하세요. 이렇게 하면 Dusk가 ChromeDriver를 자동으로 실행하지 않도록 만들 수 있습니다.

/** * Prepare for Dusk test execution. * * @beforeClass */ public static function prepare(): void { // static::startChromeDriver(); }

다음으로, driver 메서드를 수정해 원하는 URL과 포트로 연결하도록 할 수 있습니다. 또한 WebDriver에 전달할 "desired capabilities"도 함께 수정할 수 있습니다.

use Facebook\WebDriver\Remote\RemoteWebDriver; /** * Create the RemoteWebDriver instance. */ protected function driver(): RemoteWebDriver { return RemoteWebDriver::create( 'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs() ); }

Dusk

설치

WARNING

Pest 4는 이제 자동화된 브라우저 테스트 기능을 포함하고 있으며, Laravel Dusk에 비해 성능과 사용성 면에서 크게 개선되었습니다. 새로 시작하는 프로젝트라면 브라우저 테스트에 Pest를 사용하는 것을 권장합니다.

Laravel Dusk는 표현력이 풍부하고 사용하기 쉬운 브라우저 자동화 및 테스트 API를 제공합니다. Dusk는 기본적으로 로컬 컴퓨터에 JDK나 Selenium을 설치할 필요가 없습니다. 대신 독립 실행형 ChromeDriver를 사용해 브라우저를 구동합니다. 물론 원한다면 Selenium과 호환되는 다른 드라이버를 사용할 수도 있습니다.

NOTE

Dusk는 실제 브라우저를 실행해 화면을 클릭하고 입력값을 채우는 방식으로 테스트를 진행하기 때문에, 단위 테스트나 기능 테스트보다 느리지만 실제 사용자 흐름을 그대로 검증할 수 있다는 장점이 있습니다. 로그인 → 결제 → 완료 페이지 확인처럼 여러 단계를 거치는 시나리오를 검증할 때 특히 유용합니다.

Dusk

설치

먼저 구글 크롬(Google Chrome)을 설치한 다음, 프로젝트에 laravel/dusk Composer 패키지를 추가합니다:

composer require laravel/dusk --dev

WARNING

Dusk의 서비스 프로바이더를 수동으로 등록하는 경우, 프로덕션 환경에서는 절대 등록하지 마세요. 프로덕션 환경에 등록하면 임의의 사용자가 애플리케이션에 인증(로그인)할 수 있는 심각한 보안 문제가 발생할 수 있습니다.

패키지 설치 후에는 dusk:install Artisan 명령어를 실행합니다. 이 명령어는 tests/Browser 디렉터리와 예제 Dusk 테스트를 생성하고, 사용 중인 운영체제에 맞는 ChromeDriver 바이너리를 설치해 줍니다:

php artisan dusk:install

그 다음, 애플리케이션의 .env 파일에 APP_URL 환경 변수를 설정합니다. 이 값은 브라우저에서 실제로 애플리케이션에 접속할 때 사용하는 URL과 일치해야 합니다.

NOTE

로컬 개발 환경으로 Laravel Sail을 사용하고 있다면, Sail 문서의 Dusk 설정 및 실행 가이드도 함께 참고하시기 바랍니다.

ChromeDriver 버전 관리하기

dusk:install 명령어로 설치되는 버전이 아닌 다른 버전의 ChromeDriver를 설치하고 싶다면, dusk:chrome-driver 명령어를 사용하면 됩니다:

<h1 id="running-tests">현재 운영체제에 맞는 최신 버전의 ChromeDriver 설치...</h1> php artisan dusk:chrome-driver <h1 id="manually-starting-chromedriver">현재 운영체제에 맞는 특정 버전의 ChromeDriver 설치...</h1> php artisan dusk:chrome-driver 86 <h1 id="environment-handling">지원되는 모든 운영체제용 ChromeDriver 설치...</h1> php artisan dusk:chrome-driver --all <h1 id="browser-basics">현재 설치된 Chrome/Chromium 버전에 맞는 ChromeDriver를 자동 감지하여 설치...</h1> php artisan dusk:chrome-driver --detect

WARNING

Dusk가 정상적으로 동작하려면 chromedriver 바이너리에 실행 권한이 있어야 합니다. Dusk 실행 중 문제가 발생한다면, 다음 명령어로 바이너리에 실행 권한을 부여해 보세요: chmod -R 0755 vendor/laravel/dusk/bin/.

다른 브라우저 사용하기

기본적으로 Dusk는 구글 크롬과 독립 실행형 ChromeDriver를 사용해 브라우저 테스트를 수행합니다. 하지만 원한다면 직접 Selenium 서버를 실행하고, 원하는 어떤 브라우저에서든 테스트를 실행할 수도 있습니다.

시작하려면, 애플리케이션의 기본 Dusk 테스트 케이스가 정의된 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

테스트 생성하기

Dusk 테스트를 생성하려면 dusk:make Artisan 명령어를 사용합니다. 생성된 테스트 파일은 tests/Browser 디렉터리에 위치하게 됩니다:

php artisan dusk:make LoginTest

각 테스트 후 데이터베이스 초기화하기

여러분이 작성하는 대부분의 테스트는 애플리케이션 데이터베이스에서 데이터를 조회하는 페이지와 상호작용하게 됩니다. 그런데 Dusk 테스트에서는 절대로 RefreshDatabase 트레이트를 사용해서는 안 됩니다. RefreshDatabase 트레이트는 데이터베이스 트랜잭션을 기반으로 동작하는데, 이 트랜잭션은 여러 HTTP 요청에 걸쳐서는 적용될 수 없기 때문입니다. Dusk 테스트에서는 실제 브라우저가 별도의 프로세스로 애플리케이션에 요청을 보내기 때문에, 테스트 코드와 트랜잭션을 공유할 수 없다는 점을 기억해 두세요.

대신 다음 두 가지 방법 중 하나를 사용할 수 있습니다: DatabaseMigrations 트레이트와 DatabaseTruncation 트레이트입니다.

데이터베이스 마이그레이션 사용하기

DatabaseMigrations 트레이트는 각 테스트를 실행하기 전에 데이터베이스 마이그레이션을 실행합니다. 다만 매 테스트마다 테이블을 삭제하고 다시 생성하는 작업은 테이블을 비우는(truncate) 것보다 일반적으로 더 느립니다:

Pest

<?php use Illuminate\Foundation\Testing\DatabaseMigrations; pest()->use(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 인메모리 데이터베이스를 사용할 수 없습니다. 브라우저는 자신만의 별도 프로세스에서 실행되기 때문에, 테스트 코드가 실행되는 다른 프로세스의 인메모리 데이터베이스에 접근할 수 없기 때문입니다.

데이터베이스 Truncation 사용하기

DatabaseTruncation 트레이트는 첫 번째 테스트가 실행될 때 데이터베이스 마이그레이션을 수행하여 테이블이 제대로 생성되었는지 확인합니다. 이후의 테스트에서는 테이블을 다시 마이그레이션하지 않고 단순히 비우기(truncate)만 하기 때문에, 매번 전체 마이그레이션을 다시 실행하는 것보다 속도가 더 빠릅니다:

Pest

<?php use Illuminate\Foundation\Testing\DatabaseTruncation; pest()->use(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 클래스나 테스트 파일이 상속하는 클래스에 정의해야 합니다.

/** * 비워야 할(truncate) 테이블 목록을 지정합니다. * * @var array */ protected $tablesToTruncate = ['users'];

반대로, truncation 대상에서 제외할 테이블을 지정하고 싶다면 테스트 클래스에 $exceptTables 프로퍼티를 정의하면 됩니다:

/** * truncation 대상에서 제외할 테이블 목록을 지정합니다. * * @var array */ protected $exceptTables = ['users'];

테이블을 비울 데이터베이스 커넥션을 지정하려면, 테스트 클래스에 $connectionsToTruncate 프로퍼티를 정의하세요:

/** * 테이블을 truncate할 데이터베이스 커넥션 목록을 지정합니다. * * @var array */ protected $connectionsToTruncate = ['mysql'];

데이터베이스 truncation 작업 전후로 별도의 코드를 실행하고 싶다면, 테스트 클래스에 beforeTruncatingDatabase 또는 afterTruncatingDatabase 메서드를 정의하면 됩니다:

/** * 데이터베이스 truncation을 시작하기 전에 수행할 작업을 정의합니다. */ protected function beforeTruncatingDatabase(): void { // } /** * 데이터베이스 truncation이 끝난 후 수행할 작업을 정의합니다. */ protected function afterTruncatingDatabase(): void { // }

테스트 실행하기

브라우저 테스트를 실행하려면 dusk Artisan 명령어를 실행하면 됩니다:

php artisan dusk

바로 직전에 dusk 명령어를 실행했을 때 실패한 테스트가 있었다면, dusk:fails 명령어를 사용해 실패했던 테스트만 먼저 다시 실행함으로써 시간을 절약할 수 있습니다:

php artisan dusk:fails

dusk 명령어는 Pest / PHPUnit 테스트 러너가 일반적으로 지원하는 모든 인자를 그대로 받을 수 있습니다. 예를 들어 특정 그룹(group)에 속한 테스트만 실행하고 싶다면 다음과 같이 사용할 수 있습니다:

php artisan dusk --group=foo

NOTE

Laravel Sail을 사용해 로컬 개발 환경을 구성하고 있다면, Sail 문서의 Dusk 테스트 설정 및 실행 부분을 참고하시기 바랍니다.

ChromeDriver 수동으로 실행하기

기본적으로 Dusk는 ChromeDriver를 자동으로 시작하려고 시도합니다. 만약 여러분의 시스템 환경에서 이 방식이 제대로 동작하지 않는다면, dusk 명령어를 실행하기 전에 ChromeDriver를 수동으로 직접 실행할 수 있습니다. 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 파일이 다시 복원됩니다.

NOTE

즉, 평소 로컬 개발에서 사용하는 .env 파일과 Dusk 테스트에서 사용할 .env.dusk.local 파일을 분리해 두면, 테스트용 데이터베이스나 API 키 등을 실제 개발 환경과 독립적으로 관리할 수 있어 편리합니다.

Dusk

브라우저 기본 사용법

브라우저 생성하기

애플리케이션에 로그인이 정상적으로 되는지 검증하는 테스트를 작성하며 시작해 보겠습니다. 테스트를 생성한 후, 로그인 페이지로 이동해서 자격 증명을 입력하고 "Login" 버튼을 클릭하도록 수정할 수 있습니다. Dusk 테스트에서 브라우저 인스턴스를 만들려면 browse 메서드를 호출하면 됩니다:

Pest

<?php use App\Models\User; use Illuminate\Foundation\Testing\DatabaseMigrations; pest()->use(DatabaseMigrations::class); test('basic example', 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; /** * A basic browser test example. */ 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는 이 클로저에 브라우저 인스턴스를 자동으로 전달하며, 이 인스턴스가 애플리케이션과 상호작용하고 검증(assertion)을 수행하는 데 사용하는 핵심 객체입니다.

여러 개의 브라우저 생성하기

테스트를 제대로 진행하려면 여러 개의 브라우저가 필요한 경우도 있습니다. 예를 들어 웹소켓과 상호작용하는 채팅 화면을 테스트하려면 브라우저가 두 개 이상 필요할 수 있습니다. 이럴 때는 browse 메서드에 전달하는 클로저의 인자를 추가하기만 하면 여러 브라우저를 만들 수 있습니다:

$this->browse(function (Browser $first, Browser $second) { $first->loginAs(User::find(1)) ->visit('/home') ->waitForText('Message'); $second->loginAs(User::find(2)) ->visit('/home') ->waitForText('Message') ->type('message', 'Hey Taylor') ->press('Send'); $first->waitForText('Hey Taylor') ->assertSee('Jeffrey Way'); });

페이지 이동

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 { /** * Register Dusk's browser macros. */ 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('Enter Credit Card Details'); });

인증 처리하기

인증이 필요한 페이지를 테스트하는 경우가 많습니다. 매 테스트마다 로그인 화면과 직접 상호작용하는 대신, Dusk의 loginAs 메서드를 사용할 수 있습니다. loginAs 메서드는 인증 가능한(authenticatable) 모델과 연관된 기본 키(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');

script 메서드를 사용하면 브라우저 안에서 임의의 자바스크립트 구문을 실행할 수 있습니다:

$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 메서드를 사용하면 현재 페이지의 소스를 지정한 파일명으로 디스크에 기록할 수 있습니다. 페이지 소스는 tests/Browser/source 디렉터리에 저장됩니다:

$browser->storeSource('filename');

요소와 상호작용하기

Dusk 테스트를 작성할 때 가장 어려운 부분 중 하나가 바로 요소를 조작하기 위한 적절한 CSS 셀렉터를 선택하는 일입니다. 시간이 지나면서 프론트엔드가 변경되면, 다음과 같은 CSS 셀렉터는 언제든 테스트를 깨뜨릴 수 있습니다:

// HTML... <button>Login</button>
// 테스트... $browser->click('.login-page .container div > button');

Dusk 셀렉터를 사용하면 CSS 셀렉터를 일일이 외우는 대신 효과적인 테스트 작성 자체에 집중할 수 있습니다. 셀렉터를 정의하려면 HTML 요소에 dusk 속성을 추가하면 됩니다. 이후 테스트에서 Dusk 브라우저로 해당 요소를 조작할 때는 셀렉터 앞에 @를 붙이면 됩니다:

// HTML... <button dusk="login-button">Login</button>
// 테스트... $browser->click('@login-button');

필요하다면 selectorHtmlAttribute 메서드를 사용해 Dusk 셀렉터가 사용하는 HTML 속성을 원하는 이름으로 변경할 수 있습니다. 일반적으로 이 메서드는 애플리케이션의 AppServiceProviderboot 메서드에서 호출합니다:

use Laravel\Dusk\Dusk; Dusk::selectorHtmlAttribute('data-dusk');

텍스트, 값, 속성

값 가져오기와 설정하기

Dusk는 페이지 내 요소의 현재 값, 표시 텍스트, 속성과 상호작용할 수 있는 다양한 메서드를 제공합니다. 예를 들어, 주어진 CSS 또는 Dusk 셀렉터와 일치하는 요소의 "값(value)"을 가져오려면 value 메서드를 사용합니다:

// 값 가져오기... $value = $browser->value('selector'); // 값 설정하기... $browser->value('selector', 'value');

주어진 필드 이름을 가진 input 요소의 "값"을 가져오려면 inputValue 메서드를 사용할 수 있습니다:

$value = $browser->inputValue('field');

텍스트 가져오기

text 메서드를 사용하면 주어진 셀렉터와 일치하는 요소의 표시 텍스트를 가져올 수 있습니다:

$text = $browser->text('selector');

속성 가져오기

마지막으로, attribute 메서드를 사용하면 주어진 셀렉터와 일치하는 요소의 특정 속성 값을 가져올 수 있습니다:

$attribute = $browser->attribute('selector', 'value');

폼과 상호작용하기

값 입력하기

Dusk는 폼과 입력 요소를 다루기 위한 다양한 메서드를 제공합니다. 먼저, input 필드에 텍스트를 입력하는 예제를 살펴보겠습니다:

$browser->type('email', 'taylor@laravel.com');

type 메서드도 필요하다면 CSS 셀렉터를 받을 수 있지만, 반드시 전달할 필요는 없습니다. CSS 셀렉터를 전달하지 않으면 Dusk는 주어진 name 속성을 가진 input 또는 textarea 필드를 찾습니다.

필드의 기존 내용을 지우지 않고 텍스트를 뒤에 추가하고 싶다면 append 메서드를 사용하면 됩니다:

$browser->type('tags', 'foo') ->append('tags', ', bar, baz');

clear 메서드로 입력 값을 비울 수 있습니다:

$browser->clear('email');

typeSlowly 메서드를 사용하면 Dusk가 천천히 타이핑하도록 지시할 수 있습니다. 기본적으로 키 입력 사이에 100밀리초의 간격을 둡니다. 키 입력 간 시간 간격을 변경하고 싶다면 세 번째 인자로 밀리초 값을 전달하면 됩니다:

$browser->typeSlowly('mobile', '+1 (202) 555-5555'); $browser->typeSlowly('mobile', '+1 (202) 555-5555', 300);

텍스트를 천천히 추가하고 싶다면 appendSlowly 메서드를 사용할 수 있습니다:

$browser->type('tags', 'foo') ->appendSlowly('tags', ', bar, baz');

드롭다운

select 요소에서 값을 선택하려면 select 메서드를 사용하면 됩니다. type 메서드와 마찬가지로, select 메서드도 완전한 CSS 셀렉터가 필요하지 않습니다. select 메서드에 값을 전달할 때는 화면에 표시되는 텍스트가 아니라 실제 옵션 값(value)을 전달해야 합니다:

$browser->select('size', 'Large');

두 번째 인자를 생략하면 옵션 중 하나를 무작위로 선택합니다:

$browser->select('size');

select 메서드의 두 번째 인자로 배열을 전달하면 여러 옵션을 한 번에 선택하도록 지시할 수 있습니다:

$browser->select('categories', ['Art', 'Music']);

체크박스

체크박스를 "체크"하려면 check 메서드를 사용하면 됩니다. 다른 입력 관련 메서드와 마찬가지로, 완전한 CSS 셀렉터는 필요하지 않습니다. CSS 셀렉터로 일치하는 요소를 찾지 못하면, Dusk는 일치하는 name 속성을 가진 체크박스를 찾습니다:

$browser->check('terms');

체크박스를 "체크 해제"하려면 uncheck 메서드를 사용합니다:

$browser->uncheck('terms');

라디오 버튼

radio 입력 옵션을 "선택"하려면 radio 메서드를 사용하면 됩니다. 다른 입력 관련 메서드와 마찬가지로, 완전한 CSS 셀렉터는 필요하지 않습니다. CSS 셀렉터로 일치하는 요소를 찾지 못하면, Dusk는 일치하는 namevalue 속성을 가진 radio 입력 요소를 찾습니다:

$browser->radio('size', 'large');

파일 첨부하기

attach 메서드를 사용하면 file 타입 input 요소에 파일을 첨부할 수 있습니다. 다른 입력 관련 메서드와 마찬가지로, 완전한 CSS 셀렉터는 필요하지 않습니다. CSS 셀렉터로 일치하는 요소를 찾지 못하면, Dusk는 일치하는 name 속성을 가진 file 입력 요소를 찾습니다:

$browser->attach('photo', __DIR__.'/photos/mountains.png');

WARNING

attach 함수를 사용하려면 서버에 Zip PHP 확장 모듈이 설치 및 활성화되어 있어야 합니다.

버튼 클릭하기

press 메서드를 사용하면 페이지의 버튼 요소를 클릭할 수 있습니다. press 메서드에 전달하는 인자는 버튼의 표시 텍스트이거나 CSS / Dusk 셀렉터일 수 있습니다:

$browser->press('Login');

많은 애플리케이션은 폼을 제출할 때 제출 버튼을 클릭하는 즉시 비활성화하고, HTTP 요청이 완료되면 다시 활성화합니다. 버튼을 클릭한 뒤 버튼이 다시 활성화되기를 기다리려면 pressAndWaitFor 메서드를 사용할 수 있습니다:

// 버튼을 클릭하고 최대 5초간 다시 활성화되기를 기다립니다... $browser->pressAndWaitFor('Save'); // 버튼을 클릭하고 최대 1초간 다시 활성화되기를 기다립니다... $browser->pressAndWaitFor('Save', 1);

링크 클릭하기

링크를 클릭하려면 브라우저 인스턴스에서 clickLink 메서드를 사용하면 됩니다. clickLink 메서드는 주어진 표시 텍스트를 가진 링크를 클릭합니다:

$browser->clickLink($linkText);

주어진 표시 텍스트를 가진 링크가 페이지에 보이는지 확인하려면 seeLink 메서드를 사용할 수 있습니다:

if ($browser->seeLink($linkText)) { // ... }

WARNING

위 메서드들은 jQuery를 사용하여 동작합니다. 페이지에 jQuery가 없다면, Dusk가 테스트 실행 동안 사용할 수 있도록 자동으로 jQuery를 페이지에 주입합니다.

키보드 사용하기

keys 메서드를 사용하면 type 메서드로는 표현하기 어려운 복잡한 입력 시퀀스를 특정 요소에 전달할 수 있습니다. 예를 들어, 값을 입력하는 동안 보조 키(modifier key)를 누른 상태로 유지하도록 지시할 수 있습니다. 아래 예제에서는 shift 키를 누른 상태로 주어진 셀렉터와 일치하는 요소에 taylor를 입력합니다. taylor 입력이 끝나면, 보조 키 없이 swift가 입력됩니다:

$browser->keys('selector', ['{shift}', 'taylor'], 'swift');

keys 메서드의 또 다른 유용한 활용법은 애플리케이션의 주요 CSS 셀렉터로 "키보드 단축키" 조합을 전송하는 것입니다:

$browser->keys('.app', ['{command}', 'j']);

NOTE

{command}와 같은 모든 보조 키는 {} 문자로 감싸서 사용하며, 이는 Facebook\WebDriver\WebDriverKeys 클래스에 정의된 상수들과 일치합니다. 해당 클래스는 GitHub에서 확인할 수 있습니다.

플루언트 방식의 키보드 상호작용

Dusk는 Laravel\Dusk\Keyboard 클래스를 통해 복잡한 키보드 상호작용을 플루언트하게 처리할 수 있는 withKeyboard 메서드도 제공합니다. Keyboard 클래스는 press, release, type, pause 메서드를 제공합니다:

use Laravel\Dusk\Keyboard; $browser->withKeyboard(function (Keyboard $keyboard) { $keyboard->press('c') ->pause(1000) ->release('c') ->type(['c', 'e', 'o']); });

키보드 매크로

테스트 스위트 전반에서 재사용할 수 있는 커스텀 키보드 상호작용을 정의하고 싶다면, Keyboard 클래스가 제공하는 macro 메서드를 사용하면 됩니다. 일반적으로 이 메서드는 서비스 프로바이더boot 메서드에서 호출합니다:

<?php namespace App\Providers; use Facebook\WebDriver\WebDriverKeys; use Illuminate\Support\ServiceProvider; use Laravel\Dusk\Keyboard; use Laravel\Dusk\OperatingSystem; class DuskServiceProvider extends ServiceProvider { /** * Dusk의 브라우저 매크로를 등록합니다. */ public function boot(): void { Keyboard::macro('copy', function (string $element = null) { $this->type([ OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'c', ]); return $this; }); Keyboard::macro('paste', function (string $element = null) { $this->type([ OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'v', ]); return $this; }); } }

macro 함수는 첫 번째 인자로 이름을, 두 번째 인자로 클로저를 받습니다. 이렇게 등록한 매크로 클로저는 Keyboard 인스턴스에서 해당 이름의 메서드를 호출할 때 실행됩니다:

$browser->click('@textarea') ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->copy()) ->click('@another-textarea') ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->paste());

요소 클릭하기

click 메서드를 사용하면 주어진 CSS 또는 Dusk 셀렉터와 일치하는 요소를 클릭할 수 있습니다:

$browser->click('.selector');

clickAtXPath 메서드를 사용하면 주어진 XPath 표현식과 일치하는 요소를 클릭할 수 있습니다:

$browser->clickAtXPath('//div[@class = "selector"]');

clickAtPoint 메서드를 사용하면 브라우저의 보이는 영역을 기준으로, 주어진 좌표에서 가장 위에 있는 요소를 클릭할 수 있습니다:

$browser->clickAtPoint($x = 0, $y = 0);

doubleClick 메서드를 사용하면 마우스 더블 클릭을 시뮬레이션할 수 있습니다:

$browser->doubleClick(); $browser->doubleClick('.selector');

rightClick 메서드를 사용하면 마우스 오른쪽 클릭을 시뮬레이션할 수 있습니다:

$browser->rightClick(); $browser->rightClick('.selector');

clickAndHold 메서드를 사용하면 마우스 버튼을 클릭한 채로 누르고 있는 동작을 시뮬레이션할 수 있습니다. 이후 releaseMouse 메서드를 호출하면 이 동작이 해제되며 마우스 버튼을 놓습니다:

$browser->clickAndHold('.selector'); $browser->clickAndHold() ->pause(1000) ->releaseMouse();

controlClick 메서드를 사용하면 브라우저에서 ctrl+click 이벤트를 시뮬레이션할 수 있습니다:

$browser->controlClick(); $browser->controlClick('.selector');

clickWhenVisible 또는 clickWhenEnabled 메서드를 사용하면 요소가 클릭할 준비가 될 때까지 기다렸다가 정확히 한 번 클릭할 수 있습니다:

$browser->clickWhenVisible('@save-button'); $browser->clickWhenEnabled('@submit-button');

마우스 오버

mouseover 메서드는 주어진 CSS 또는 Dusk 셀렉터와 일치하는 요소 위로 마우스를 이동시킬 때 사용합니다:

$browser->mouseover('.selector');

드래그 앤 드롭

drag 메서드를 사용하면 주어진 셀렉터와 일치하는 요소를 다른 요소로 드래그할 수 있습니다:

$browser->drag('.from-selector', '.to-selector');

또는, 하나의 방향으로만 요소를 드래그할 수도 있습니다:

$browser->dragLeft('.selector', $pixels = 10); $browser->dragRight('.selector', $pixels = 10); $browser->dragUp('.selector', $pixels = 10); $browser->dragDown('.selector', $pixels = 10);

마지막으로, 주어진 오프셋만큼 요소를 드래그할 수도 있습니다:

$browser->dragOffset('.selector', $x = 10, $y = 10);

JavaScript 다이얼로그

Dusk는 JavaScript 다이얼로그와 상호작용할 수 있는 다양한 메서드를 제공합니다. 예를 들어, waitForDialog 메서드를 사용하면 JavaScript 다이얼로그가 나타날 때까지 기다릴 수 있습니다. 이 메서드는 다이얼로그가 나타나기를 기다릴 최대 초를 선택적으로 인자로 받습니다:

$browser->waitForDialog($seconds = null);

assertDialogOpened 메서드는 다이얼로그가 표시되었고, 주어진 메시지를 포함하고 있는지 단언할 때 사용합니다:

$browser->assertDialogOpened('Dialog message');

JavaScript 다이얼로그에 프롬프트(입력창)가 있다면, typeInDialog 메서드를 사용해 프롬프트에 값을 입력할 수 있습니다:

$browser->typeInDialog('Hello World');

"OK" 버튼을 클릭해 열려 있는 JavaScript 다이얼로그를 닫으려면 acceptDialog 메서드를 호출하면 됩니다:

$browser->acceptDialog();

"Cancel" 버튼을 클릭해 열려 있는 JavaScript 다이얼로그를 닫으려면 dismissDialog 메서드를 호출하면 됩니다:

$browser->dismissDialog();

인라인 프레임과 상호작용하기

iframe 내부의 요소와 상호작용해야 한다면 withinFrame 메서드를 사용할 수 있습니다. withinFrame 메서드에 전달한 클로저 안에서 이루어지는 모든 요소 상호작용은 지정한 iframe의 컨텍스트로 범위가 한정됩니다:

$browser->withinFrame('#credit-card-details', function ($browser) { $browser->type('input[name="cardnumber"]', '4242424242424242') ->type('input[name="exp-date"]', '1224') ->type('input[name="cvc"]', '123') ->press('Pay'); });

셀렉터 범위 지정하기

경우에 따라 특정 셀렉터 범위 내에서 여러 작업을 연속으로 수행하고 싶을 때가 있습니다. 예를 들어, 어떤 텍스트가 테이블 안에만 존재하는지 확인한 뒤, 해당 테이블 안의 버튼을 클릭하고 싶을 수 있습니다. 이럴 때는 with 메서드를 사용하면 됩니다. with 메서드에 전달한 클로저 안에서 수행되는 모든 작업은 원래 셀렉터의 범위로 한정됩니다:

$browser->with('.table', function (Browser $table) { $table->assertSee('Hello World') ->clickLink('Delete'); });

가끔은 현재 범위를 벗어나 단언(assertion)을 수행해야 할 수도 있습니다. 이럴 때는 elsewhereelsewhereWhenAvailable 메서드를 사용하면 됩니다:

$browser->with('.table', function (Browser $table) { // 현재 범위는 `body .table`입니다... $browser->elsewhere('.page-title', function (Browser $title) { // 현재 범위는 `body .page-title`입니다... $title->assertSee('Hello World'); }); $browser->elsewhereWhenAvailable('.page-title', function (Browser $title) { // 현재 범위는 `body .page-title`입니다... $title->assertSee('Hello World'); }); });

요소를 기다리기

JavaScript를 많이 사용하는 애플리케이션을 테스트하다 보면, 테스트를 계속 진행하기 전에 특정 요소나 데이터가 준비될 때까지 "기다려야" 하는 경우가 자주 발생합니다. Dusk는 이를 아주 쉽게 처리할 수 있도록 도와줍니다. 다양한 메서드를 사용하여 요소가 페이지에 나타날 때까지 기다리거나, 특정 JavaScript 표현식이 true로 평가될 때까지 기다릴 수 있습니다.

대기하기

단순히 주어진 밀리초만큼 테스트를 일시 정지하고 싶다면 pause 메서드를 사용하면 됩니다:

$browser->pause(1000);

주어진 조건이 true일 때만 테스트를 일시 정지하고 싶다면 pauseIf 메서드를 사용하면 됩니다:

$browser->pauseIf(App::environment('production'), 1000);

반대로, 주어진 조건이 true가 아닐 때만 테스트를 일시 정지하고 싶다면 pauseUnless 메서드를 사용할 수 있습니다:

$browser->pauseUnless(App::environment('testing'), 1000);

셀렉터를 기다리기

waitFor 메서드를 사용하면 주어진 CSS 또는 Dusk 셀렉터와 일치하는 요소가 페이지에 표시될 때까지 테스트 실행을 일시 정지할 수 있습니다. 기본적으로 최대 5초까지 기다리며, 이 시간 안에 요소가 나타나지 않으면 예외가 발생합니다. 필요하다면 두 번째 인자로 원하는 대기 시간(초)을 전달할 수 있습니다:

// 최대 5초간 셀렉터를 기다립니다... $browser->waitFor('.selector'); // 최대 1초간 셀렉터를 기다립니다... $browser->waitFor('.selector', 1);

주어진 셀렉터와 일치하는 요소가 특정 텍스트를 포함할 때까지 기다릴 수도 있습니다:

// 최대 5초간 셀렉터가 주어진 텍스트를 포함할 때까지 기다립니다... $browser->waitForTextIn('.selector', 'Hello World'); // 최대 1초간 셀렉터가 주어진 텍스트를 포함할 때까지 기다립니다... $browser->waitForTextIn('.selector', 'Hello World', 1);

주어진 셀렉터와 일치하는 요소가 페이지에서 사라질 때까지 기다릴 수도 있습니다:

// 최대 5초간 셀렉터가 사라질 때까지 기다립니다... $browser->waitUntilMissing('.selector'); // 최대 1초간 셀렉터가 사라질 때까지 기다립니다... $browser->waitUntilMissing('.selector', 1);

또는, 주어진 셀렉터와 일치하는 요소가 활성화되거나 비활성화될 때까지 기다릴 수도 있습니다:

// 최대 5초간 셀렉터가 활성화될 때까지 기다립니다... $browser->waitUntilEnabled('.selector'); // 최대 1초간 셀렉터가 활성화될 때까지 기다립니다... $browser->waitUntilEnabled('.selector', 1); // 최대 5초간 셀렉터가 비활성화될 때까지 기다립니다... $browser->waitUntilDisabled('.selector'); // 최대 1초간 셀렉터가 비활성화될 때까지 기다립니다... $browser->waitUntilDisabled('.selector', 1);

사용 가능해지면 셀렉터 범위 지정하기

경우에 따라, 주어진 셀렉터와 일치하는 요소가 나타나기를 기다렸다가 곧바로 해당 요소와 상호작용하고 싶을 수 있습니다. 예를 들어, 모달 창이 나타날 때까지 기다렸다가 모달 안의 "OK" 버튼을 누르고 싶을 때입니다. 이럴 때는 whenAvailable 메서드를 사용하면 됩니다. 주어진 클로저 안에서 수행되는 모든 요소 작업은 원래 셀렉터의 범위로 한정됩니다:

$browser->whenAvailable('.modal', function (Browser $modal) { $modal->assertSee('Hello World') ->press('OK'); });

텍스트를 기다리기

waitForText 메서드는 주어진 텍스트가 페이지에 표시될 때까지 기다릴 때 사용합니다:

// 최대 5초간 텍스트를 기다립니다... $browser->waitForText('Hello World'); // 최대 1초간 텍스트를 기다립니다... $browser->waitForText('Hello World', 1);

표시된 텍스트가 페이지에서 제거될 때까지 기다리려면 waitUntilMissingText 메서드를 사용할 수 있습니다:

// 최대 5초간 텍스트가 제거될 때까지 기다립니다... $browser->waitUntilMissingText('Hello World'); // 최대 1초간 텍스트가 제거될 때까지 기다립니다... $browser->waitUntilMissingText('Hello World', 1);

링크를 기다리기

waitForLink 메서드는 주어진 링크 텍스트가 페이지에 표시될 때까지 기다릴 때 사용합니다:

// 최대 5초간 링크를 기다립니다... $browser->waitForLink('Create'); // 최대 1초간 링크를 기다립니다... $browser->waitForLink('Create', 1);

입력 필드를 기다리기

waitForInput 메서드는 주어진 입력 필드가 페이지에 보일 때까지 기다릴 때 사용합니다:

// 최대 5초간 입력 필드를 기다립니다... $browser->waitForInput($field); // 최대 1초간 입력 필드를 기다립니다... $browser->waitForInput($field, 1);

페이지 위치(location) 기다리기

$browser->assertPathIs('/home')와 같은 경로 단언을 수행할 때, window.location.pathname이 비동기적으로 갱신되는 중이라면 단언이 실패할 수 있습니다. 이럴 때는 waitForLocation 메서드를 사용해 위치가 주어진 값이 될 때까지 기다릴 수 있습니다:

$browser->waitForLocation('/secret');

waitForLocation 메서드는 현재 창의 위치가 완전한 형태의 URL이 될 때까지 기다리는 데도 사용할 수 있습니다:

$browser->waitForLocation('https://example.com/path');

이름이 지정된 라우트의 위치를 기다릴 수도 있습니다:

$browser->waitForRoute($routeName, $parameters);

페이지 새로고침 기다리기

어떤 동작을 수행한 뒤 페이지가 새로고침되기를 기다려야 한다면 waitForReload 메서드를 사용하면 됩니다:

use Laravel\Dusk\Browser; $browser->waitForReload(function (Browser $browser) { $browser->press('Submit'); }) ->assertSee('Success!');

페이지 새로고침을 기다려야 하는 경우는 대부분 버튼 클릭 직후에 발생하므로, 편의를 위해 clickAndWaitForReload 메서드를 사용할 수도 있습니다:

$browser->clickAndWaitForReload('.selector') ->assertSee('something');

JavaScript 표현식 기다리기

때로는 주어진 JavaScript 표현식이 true로 평가될 때까지 테스트 실행을 일시 정지하고 싶을 수 있습니다. 이럴 때는 waitUntil 메서드를 사용하면 쉽게 처리할 수 있습니다. 이 메서드에 표현식을 전달할 때는 return 키워드나 마지막 세미콜론을 포함할 필요가 없습니다:

// 최대 5초간 표현식이 true가 될 때까지 기다립니다... $browser->waitUntil('App.data.servers.length > 0'); // 최대 1초간 표현식이 true가 될 때까지 기다립니다... $browser->waitUntil('App.data.servers.length > 0', 1);

Vue 표현식 기다리기

waitUntilVuewaitUntilVueIsNot 메서드는 Vue 컴포넌트의 특정 속성이 주어진 값을 가질 때까지 기다릴 때 사용합니다:

// 컴포넌트 속성이 주어진 값을 포함할 때까지 기다립니다... $browser->waitUntilVue('user.name', 'Taylor', '@user'); // 컴포넌트 속성이 주어진 값을 포함하지 않을 때까지 기다립니다... $browser->waitUntilVueIsNot('user.name', null, '@user');

JavaScript 이벤트 기다리기

waitForEvent 메서드를 사용하면 JavaScript 이벤트가 발생할 때까지 테스트 실행을 일시 정지할 수 있습니다:

$browser->waitForEvent('load');

이벤트 리스너는 현재 범위(scope)에 등록되며, 기본값은 body 요소입니다. 범위가 지정된 셀렉터를 사용 중이라면, 이벤트 리스너는 해당 일치 요소에 등록됩니다:

$browser->with('iframe', function (Browser $iframe) { // iframe의 load 이벤트를 기다립니다... $iframe->waitForEvent('load'); });

waitForEvent 메서드의 두 번째 인자로 셀렉터를 전달하여 특정 요소에 이벤트 리스너를 등록할 수도 있습니다:

$browser->waitForEvent('load', '.selector');

documentwindow 객체의 이벤트를 기다릴 수도 있습니다:

// document가 스크롤될 때까지 기다립니다... $browser->waitForEvent('scroll', 'document'); // 최대 5초간 window의 리사이즈를 기다립니다... $browser->waitForEvent('resize', 'window', 5);

콜백으로 기다리기

Dusk가 제공하는 많은 "wait" 계열 메서드는 내부적으로 waitUsing 메서드를 기반으로 동작합니다. 이 메서드를 직접 사용하면 주어진 클로저가 true를 반환할 때까지 기다릴 수 있습니다. waitUsing 메서드는 최대 대기 시간(초), 클로저를 평가할 간격, 클로저, 그리고 선택적으로 실패 메시지를 인자로 받습니다:

$browser->waitUsing(10, 1, function () use ($something) { return $something->isReady(); }, "Something wasn't ready in time.");

요소를 화면에 보이도록 스크롤하기

브라우저의 보이는 영역 밖에 있는 요소는 클릭할 수 없는 경우가 있습니다. scrollIntoView 메서드를 사용하면 주어진 셀렉터의 요소가 화면 안에 들어올 때까지 브라우저 창을 스크롤할 수 있습니다:

$browser->scrollIntoView('.selector') ->click('.selector');

사용 가능한 어설션

Dusk는 애플리케이션에 대해 사용할 수 있는 다양한 어설션(assertion)을 제공합니다. 아래 목록에서 사용 가능한 모든 어설션을 확인할 수 있습니다.

assertTitle

페이지 제목이 주어진 텍스트와 일치하는지 확인합니다.

$browser->assertTitle($title);

assertTitleContains

페이지 제목에 주어진 텍스트가 포함되어 있는지 확인합니다.

$browser->assertTitleContains($title);

assertUrlIs

현재 URL(쿼리 문자열 제외)이 주어진 문자열과 일치하는지 확인합니다.

$browser->assertUrlIs($url);

assertSchemeIs

현재 URL의 스킴(scheme)이 주어진 스킴과 일치하는지 확인합니다.

$browser->assertSchemeIs($scheme);

assertSchemeIsNot

현재 URL의 스킴이 주어진 스킴과 일치하지 않는지 확인합니다.

$browser->assertSchemeIsNot($scheme);

assertHostIs

현재 URL의 호스트가 주어진 호스트와 일치하는지 확인합니다.

$browser->assertHostIs($host);

assertHostIsNot

현재 URL의 호스트가 주어진 호스트와 일치하지 않는지 확인합니다.

$browser->assertHostIsNot($host);

assertPortIs

현재 URL의 포트가 주어진 포트와 일치하는지 확인합니다.

$browser->assertPortIs($port);

현재 URL의 포트가 주어진 포트와 일치하지 않는지 확인합니다.

$browser->assertPortIsNot($port);

현재 URL 경로가 주어진 경로로 시작하는지 확인합니다.

$browser->assertPathBeginsWith('/home');

현재 URL 경로가 주어진 경로로 끝나는지 확인합니다.

$browser->assertPathEndsWith('/home');

현재 URL 경로에 주어진 경로가 포함되어 있는지 확인합니다.

$browser->assertPathContains('/home');

현재 경로가 주어진 경로와 일치하는지 확인합니다.

$browser->assertPathIs('/home');

현재 경로가 주어진 경로와 일치하지 않는지 확인합니다.

$browser->assertPathIsNot('/home');

assertRouteIs

현재 URL이 주어진 이름이 지정된 라우트의 URL과 일치하는지 확인합니다.

$browser->assertRouteIs($name, $parameters);

assertQueryStringHas

주어진 쿼리 문자열 파라미터가 존재하는지 확인합니다.

$browser->assertQueryStringHas($name);

주어진 쿼리 문자열 파라미터가 존재하고, 지정한 값을 가지고 있는지 확인합니다.

$browser->assertQueryStringHas($name, $value);

assertQueryStringMissing

주어진 쿼리 문자열 파라미터가 존재하지 않는지 확인합니다.

$browser->assertQueryStringMissing($name);

assertFragmentIs

URL의 현재 해시 프래그먼트(hash fragment)가 주어진 프래그먼트와 일치하는지 확인합니다.

$browser->assertFragmentIs('anchor');

assertFragmentBeginsWith

URL의 현재 해시 프래그먼트가 주어진 프래그먼트로 시작하는지 확인합니다.

$browser->assertFragmentBeginsWith('anchor');

assertFragmentIsNot

URL의 현재 해시 프래그먼트가 주어진 프래그먼트와 일치하지 않는지 확인합니다.

$browser->assertFragmentIsNot('anchor');

assertHasCookie

주어진 암호화된 쿠키가 존재하는지 확인합니다.

$browser->assertHasCookie($name);

assertHasPlainCookie

주어진 암호화되지 않은 쿠키가 존재하는지 확인합니다.

$browser->assertHasPlainCookie($name);

assertCookieMissing

주어진 암호화된 쿠키가 존재하지 않는지 확인합니다.

$browser->assertCookieMissing($name);

assertPlainCookieMissing

주어진 암호화되지 않은 쿠키가 존재하지 않는지 확인합니다.

$browser->assertPlainCookieMissing($name);

암호화된 쿠키가 주어진 값을 가지고 있는지 확인합니다.

$browser->assertCookieValue($name, $value);

암호화되지 않은 쿠키가 주어진 값을 가지고 있는지 확인합니다.

$browser->assertPlainCookieValue($name, $value);

assertSee

주어진 텍스트가 페이지에 존재하는지 확인합니다.

$browser->assertSee($text);

assertDontSee

주어진 텍스트가 페이지에 존재하지 않는지 확인합니다.

$browser->assertDontSee($text);

assertSeeIn

주어진 텍스트가 지정한 선택자(selector) 내부에 존재하는지 확인합니다.

$browser->assertSeeIn($selector, $text);

assertDontSeeIn

주어진 텍스트가 지정한 선택자 내부에 존재하지 않는지 확인합니다.

$browser->assertDontSeeIn($selector, $text);

assertSeeAnythingIn

지정한 선택자 내부에 어떤 텍스트든 존재하는지 확인합니다.

$browser->assertSeeAnythingIn($selector);

assertSeeNothingIn

지정한 선택자 내부에 텍스트가 전혀 존재하지 않는지 확인합니다.

$browser->assertSeeNothingIn($selector);

assertCount

주어진 선택자와 일치하는 요소가 지정한 횟수만큼 나타나는지 확인합니다.

$browser->assertCount($selector, $count);

assertScript

주어진 자바스크립트 표현식이 지정한 값으로 평가되는지 확인합니다.

$browser->assertScript('window.isLoaded') ->assertScript('document.readyState', 'complete');

assertSourceHas

주어진 소스 코드가 페이지에 존재하는지 확인합니다.

$browser->assertSourceHas($code);

assertSourceMissing

주어진 소스 코드가 페이지에 존재하지 않는지 확인합니다.

$browser->assertSourceMissing($code);

assertSeeLink

주어진 링크가 페이지에 존재하는지 확인합니다.

$browser->assertSeeLink($linkText);

assertDontSeeLink

주어진 링크가 페이지에 존재하지 않는지 확인합니다.

$browser->assertDontSeeLink($linkText);

assertInputValue

주어진 입력 필드가 지정한 값을 가지고 있는지 확인합니다.

$browser->assertInputValue($field, $value);

assertInputValueIsNot

주어진 입력 필드가 지정한 값을 가지고 있지 않은지 확인합니다.

$browser->assertInputValueIsNot($field, $value);

assertChecked

주어진 체크박스가 선택되어 있는지 확인합니다.

$browser->assertChecked($field);

assertNotChecked

주어진 체크박스가 선택되어 있지 않은지 확인합니다.

$browser->assertNotChecked($field);

assertIndeterminate

주어진 체크박스가 불확정(indeterminate) 상태인지 확인합니다.

$browser->assertIndeterminate($field);

assertRadioSelected

주어진 라디오 필드가 선택되어 있는지 확인합니다.

$browser->assertRadioSelected($field, $value);

assertRadioNotSelected

주어진 라디오 필드가 선택되어 있지 않은지 확인합니다.

$browser->assertRadioNotSelected($field, $value);

assertSelected

주어진 드롭다운에서 지정한 값이 선택되어 있는지 확인합니다.

$browser->assertSelected($field, $value);

assertNotSelected

주어진 드롭다운에서 지정한 값이 선택되어 있지 않은지 확인합니다.

$browser->assertNotSelected($field, $value);

assertSelectHasOptions

주어진 값 배열이 선택 가능한 옵션으로 존재하는지 확인합니다.

$browser->assertSelectHasOptions($field, $values);

assertSelectMissingOptions

주어진 값 배열이 선택 가능한 옵션으로 존재하지 않는지 확인합니다.

$browser->assertSelectMissingOptions($field, $values);

assertSelectHasOption

주어진 값이 지정한 필드에서 선택 가능한지 확인합니다.

$browser->assertSelectHasOption($field, $value);

assertSelectMissingOption

주어진 값이 선택 가능한 옵션으로 존재하지 않는지 확인합니다.

$browser->assertSelectMissingOption($field, $value);

assertValue

주어진 선택자와 일치하는 요소가 지정한 값을 가지고 있는지 확인합니다.

$browser->assertValue($selector, $value);

assertValueIsNot

주어진 선택자와 일치하는 요소가 지정한 값을 가지고 있지 않은지 확인합니다.

$browser->assertValueIsNot($selector, $value);

assertAttribute

주어진 선택자와 일치하는 요소의 지정한 속성(attribute)이 주어진 값을 가지고 있는지 확인합니다.

$browser->assertAttribute($selector, $attribute, $value);

assertAttributeMissing

주어진 선택자와 일치하는 요소에 지정한 속성이 존재하지 않는지 확인합니다.

$browser->assertAttributeMissing($selector, $attribute);

assertAttributeContains

주어진 선택자와 일치하는 요소의 지정한 속성에 주어진 값이 포함되어 있는지 확인합니다.

$browser->assertAttributeContains($selector, $attribute, $value);

assertAttributeDoesntContain

주어진 선택자와 일치하는 요소의 지정한 속성에 주어진 값이 포함되어 있지 않은지 확인합니다.

$browser->assertAttributeDoesntContain($selector, $attribute, $value);

assertAriaAttribute

주어진 선택자와 일치하는 요소의 지정한 aria 속성이 주어진 값을 가지고 있는지 확인합니다.

$browser->assertAriaAttribute($selector, $attribute, $value);

예를 들어, <button aria-label="Add"></button> 마크업이 있다면 다음과 같이 aria-label 속성을 검증할 수 있습니다.

$browser->assertAriaAttribute('button', 'label', 'Add');

assertDataAttribute

주어진 선택자와 일치하는 요소의 지정한 data 속성이 주어진 값을 가지고 있는지 확인합니다.

$browser->assertDataAttribute($selector, $attribute, $value);

예를 들어, <tr id="row-1" data-content="attendees"></tr> 마크업이 있다면 다음과 같이 data-content 속성을 검증할 수 있습니다.

$browser->assertDataAttribute('#row-1', 'content', 'attendees');

assertVisible

주어진 선택자와 일치하는 요소가 화면에 보이는지 확인합니다.

$browser->assertVisible($selector);

assertPresent

주어진 선택자와 일치하는 요소가 소스 코드 상에 존재하는지 확인합니다.

$browser->assertPresent($selector);

assertNotPresent

주어진 선택자와 일치하는 요소가 소스 코드 상에 존재하지 않는지 확인합니다.

$browser->assertNotPresent($selector);

assertMissing

주어진 선택자와 일치하는 요소가 화면에 보이지 않는지 확인합니다.

$browser->assertMissing($selector);

assertInputPresent

지정한 이름을 가진 입력(input) 요소가 존재하는지 확인합니다.

$browser->assertInputPresent($name);

assertInputMissing

지정한 이름을 가진 입력 요소가 소스 코드 상에 존재하지 않는지 확인합니다.

$browser->assertInputMissing($name);

assertDialogOpened

주어진 메시지를 가진 자바스크립트 다이얼로그(alert, confirm 등)가 열렸는지 확인합니다.

$browser->assertDialogOpened($message);

assertEnabled

주어진 필드가 활성화(enabled)되어 있는지 확인합니다.

$browser->assertEnabled($field);

assertDisabled

주어진 필드가 비활성화(disabled)되어 있는지 확인합니다.

$browser->assertDisabled($field);

assertButtonEnabled

주어진 버튼이 활성화되어 있는지 확인합니다.

$browser->assertButtonEnabled($button);

assertButtonDisabled

주어진 버튼이 비활성화되어 있는지 확인합니다.

$browser->assertButtonDisabled($button);

assertFocused

주어진 필드가 포커스되어 있는지 확인합니다.

$browser->assertFocused($field);

assertNotFocused

주어진 필드가 포커스되어 있지 않은지 확인합니다.

$browser->assertNotFocused($field);

사용자가 인증(로그인)된 상태인지 확인합니다.

$browser->assertAuthenticated();

assertGuest

사용자가 인증되지 않은 상태(게스트)인지 확인합니다.

$browser->assertGuest();

assertAuthenticatedAs

사용자가 주어진 사용자로 인증되어 있는지 확인합니다.

$browser->assertAuthenticatedAs($user);

assertVue

Dusk는 Vue 컴포넌트의 데이터 상태에 대해서도 어설션을 수행할 수 있습니다. 예를 들어 애플리케이션에 다음과 같은 Vue 컴포넌트가 있다고 가정해 보겠습니다.

// HTML... <profile dusk="profile-component"></profile> // 컴포넌트 정의... Vue.component('profile', { template: '<div>{{ user.name }}</div>', data: function () { return { user: { name: 'Taylor' } }; } });

다음과 같이 Vue 컴포넌트의 상태를 검증할 수 있습니다.

Pest

test('vue', function () { $this->browse(function (Browser $browser) { $browser->visit('/') ->assertVue('user.name', 'Taylor', '@profile-component'); }); });

PHPUnit

/** * 기본적인 Vue 테스트 예제입니다. */ public function test_vue(): void { $this->browse(function (Browser $browser) { $browser->visit('/') ->assertVue('user.name', 'Taylor', '@profile-component'); }); }

NOTE

assertVue와 관련 어설션들은 애플리케이션에서 Vue를 사용하는 경우에만 유효합니다. React, Alpine.js 등 다른 프론트엔드 프레임워크를 사용한다면 이 어설션 대신 assertScript나 일반적인 DOM 관련 어설션을 활용하는 것이 좋습니다.

assertVueIsNot

주어진 Vue 컴포넌트 데이터 속성이 지정한 값과 일치하지 않는지 확인합니다.

$browser->assertVueIsNot($property, $value, $componentSelector = null);

assertVueContains

주어진 Vue 컴포넌트 데이터 속성이 배열이며, 지정한 값을 포함하고 있는지 확인합니다.

$browser->assertVueContains($property, $value, $componentSelector = null);

assertVueDoesntContain

주어진 Vue 컴포넌트 데이터 속성이 배열이며, 지정한 값을 포함하고 있지 않은지 확인합니다.

$browser->assertVueDoesntContain($property, $value, $componentSelector = null);

Dusk

페이지(Page) 객체

테스트에서 여러 단계의 복잡한 동작을 순서대로 수행해야 할 때가 있습니다. 이런 경우 테스트 코드가 길어지고 읽기 어려워지기 마련입니다. Dusk의 페이지(Page) 객체를 사용하면 특정 페이지에서 수행할 동작들을 하나의 메서드로 표현력 있게 정의할 수 있습니다. 또한 애플리케이션 전체 또는 특정 페이지에서 자주 쓰는 셀렉터에 대한 단축 표현(shorthand)을 정의할 수도 있습니다.

페이지 생성하기

페이지 객체는 dusk:page Artisan 명령어로 생성합니다. 생성된 페이지 객체는 애플리케이션의 tests/Browser/Pages 디렉터리에 저장됩니다:

php artisan dusk:page Login

페이지 구성하기

기본적으로 페이지 클래스에는 url, assert, elements라는 세 가지 메서드가 존재합니다. 여기서는 먼저 urlassert 메서드를 살펴보고, elements 메서드는 아래에서 자세히 다룹니다.

`url` 메서드

url 메서드는 해당 페이지를 나타내는 URL 경로를 반환해야 합니다. Dusk는 브라우저에서 이 페이지로 이동할 때 이 URL을 사용합니다:

/** * 페이지의 URL을 가져옵니다. */ public function url(): string { return '/login'; }

assert 메서드

assert 메서드는 브라우저가 실제로 해당 페이지에 있는지 검증하는 데 필요한 어써션을 작성하는 곳입니다. 반드시 무언가를 작성해야 하는 것은 아니지만, 원한다면 자유롭게 어써션을 추가할 수 있습니다. 이 어써션들은 페이지로 이동할 때 자동으로 실행됩니다:

/** * 브라우저가 해당 페이지에 있는지 확인합니다. */ public function assert(Browser $browser): void { $browser->assertPathIs($this->url()); }

페이지로 이동하기

페이지를 정의했다면, visit 메서드를 사용해 해당 페이지로 이동할 수 있습니다:

use Tests\Browser\Pages\Login; $browser->visit(new Login);

경우에 따라 이미 특정 페이지에 있는 상태에서, 현재 테스트 컨텍스트에 그 페이지의 셀렉터와 메서드를 "로드"해야 할 때가 있습니다. 버튼을 클릭한 후 명시적으로 visit을 호출하지 않고도 특정 페이지로 리다이렉트되는 경우가 대표적입니다. 이런 상황에서는 on 메서드를 사용해 페이지를 로드할 수 있습니다:

use Tests\Browser\Pages\CreatePlaylist; $browser->visit('/dashboard') ->clickLink('Create Playlist') ->on(new CreatePlaylist) ->assertSee('@create');

단축 셀렉터(Shorthand Selector)

페이지 클래스의 elements 메서드를 사용하면 페이지 내 CSS 셀렉터에 대해 빠르고 기억하기 쉬운 단축 표현을 정의할 수 있습니다. 예를 들어 로그인 페이지의 "email" 입력 필드에 대한 단축 셀렉터를 정의해 보겠습니다:

/** * 페이지에서 사용할 엘리먼트 단축 셀렉터를 가져옵니다. * * @return array<string, string> */ public function elements(): array { return [ '@email' => 'input[name=email]', ]; }

이렇게 단축 셀렉터를 정의하고 나면, 일반적으로 전체 CSS 셀렉터를 쓰던 곳 어디에서든 이 단축 셀렉터를 사용할 수 있습니다:

$browser->type('@email', 'taylor@laravel.com');

전역 단축 셀렉터

Dusk를 설치하면 tests/Browser/Pages 디렉터리에 기본 Page 클래스가 생성됩니다. 이 클래스에는 siteElements 메서드가 포함되어 있으며, 애플리케이션의 모든 페이지에서 공통으로 사용할 전역 단축 셀렉터를 정의할 때 활용할 수 있습니다:

/** * 사이트 전체에서 사용할 전역 엘리먼트 단축 셀렉터를 가져옵니다. * * @return array<string, string> */ public static function siteElements(): array { return [ '@element' => '#selector', ]; }

페이지 메서드

페이지 클래스에는 기본으로 제공되는 메서드 외에도, 테스트 전반에서 활용할 수 있는 추가 메서드를 자유롭게 정의할 수 있습니다. 예를 들어 음악 관리 애플리케이션을 만들고 있다고 가정해 보겠습니다. 이 애플리케이션의 특정 페이지에서 자주 수행되는 동작 중 하나가 "플레이리스트 생성"이라면, 매번 테스트마다 플레이리스트 생성 로직을 반복해서 작성하는 대신 페이지 클래스에 createPlaylist 메서드를 정의해 재사용할 수 있습니다:

<?php namespace Tests\Browser\Pages; use Laravel\Dusk\Browser; use Laravel\Dusk\Page; class Dashboard extends Page { // 다른 페이지 메서드들... /** * 새 플레이리스트를 생성합니다. */ public function createPlaylist(Browser $browser, string $name): void { $browser->type('name', $name) ->check('share') ->press('Create Playlist'); } }

메서드를 정의한 뒤에는, 해당 페이지를 사용하는 어떤 테스트에서든 이 메서드를 호출할 수 있습니다. 이때 브라우저 인스턴스는 커스텀 페이지 메서드의 첫 번째 인자로 자동 전달됩니다:

use Tests\Browser\Pages\Dashboard; $browser->visit(new Dashboard) ->createPlaylist('My Playlist') ->assertSee('My Playlist');

Components (컴포넌트)

컴포넌트는 Dusk의 "페이지 객체"와 비슷한 개념이지만, 특정 URL에 종속되지 않고 애플리케이션 전반에서 반복적으로 사용되는 UI나 기능 조각을 위한 것입니다. 예를 들어 내비게이션 바나 알림 창처럼 여러 페이지에 걸쳐 등장하는 요소가 대표적인 사례입니다.

NOTE

페이지(Page)는 "특정 화면 하나"를 나타내고, 컴포넌트(Component)는 "여러 화면에서 재사용되는 UI 조각"을 나타낸다고 이해하면 구분하기 쉽습니다.

컴포넌트 생성하기

컴포넌트를 생성하려면 dusk:component Artisan 명령어를 실행하면 됩니다. 새로 생성된 컴포넌트는 tests/Browser/Components 디렉터리에 위치합니다.

php artisan dusk:component DatePicker

위 예시에서 볼 수 있듯, "날짜 선택기(date picker)"는 애플리케이션의 여러 페이지에서 공통으로 사용될 법한 컴포넌트의 좋은 예입니다. 테스트 코드마다 날짜를 선택하는 브라우저 자동화 로직을 매번 직접 작성한다면 수십 개의 테스트에서 똑같은 코드를 반복하게 되어 유지보수가 번거로워집니다. 이럴 때 Dusk 컴포넌트로 날짜 선택기를 표현하면, 관련 로직을 컴포넌트 하나에 캡슐화할 수 있습니다.

<?php namespace Tests\Browser\Components; use Laravel\Dusk\Browser; use Laravel\Dusk\Component as BaseComponent; class DatePicker extends BaseComponent { /** * 컴포넌트의 루트 셀렉터를 반환합니다. */ public function selector(): string { return '.date-picker'; } /** * 브라우저 페이지에 이 컴포넌트가 포함되어 있는지 검증합니다. */ public function assert(Browser $browser): void { $browser->assertVisible($this->selector()); } /** * 컴포넌트에서 사용할 엘리먼트 단축키(shortcut)를 반환합니다. * * @return array<string, string> */ public function elements(): array { return [ '@date-field' => 'input.datepicker-input', '@year-list' => 'div > div.datepicker-years', '@month-list' => 'div > div.datepicker-months', '@day-list' => 'div > div.datepicker-days', ]; } /** * 지정한 날짜를 선택합니다. */ public function selectDate(Browser $browser, int $year, int $month, int $day): void { $browser->click('@date-field') ->within('@year-list', function (Browser $browser) use ($year) { $browser->click($year); }) ->within('@month-list', function (Browser $browser) use ($month) { $browser->click($month); }) ->within('@day-list', function (Browser $browser) use ($day) { $browser->click($day); }); } }

컴포넌트 사용하기

컴포넌트를 정의하고 나면, 어떤 테스트에서든 날짜 선택기 안에서 손쉽게 날짜를 선택할 수 있습니다. 또한 날짜를 선택하는 방식이 나중에 바뀌더라도 컴포넌트 코드만 수정하면 되므로, 이를 사용하는 모든 테스트를 일일이 고칠 필요가 없습니다.

Pest

<?php use Illuminate\Foundation\Testing\DatabaseMigrations; use Tests\Browser\Components\DatePicker; pest()->use(DatabaseMigrations::class); test('basic example', function () { $this->browse(function (Browser $browser) { $browser->visit('/') ->within(new DatePicker, function (Browser $browser) { $browser->selectDate(2019, 1, 30); }) ->assertSee('January'); }); });

PHPUnit

<?php namespace Tests\Browser; use Illuminate\Foundation\Testing\DatabaseMigrations; use Laravel\Dusk\Browser; use Tests\Browser\Components\DatePicker; use Tests\DuskTestCase; class ExampleTest extends DuskTestCase { /** * 컴포넌트를 사용하는 기본 테스트 예시입니다. */ public function test_basic_example(): void { $this->browse(function (Browser $browser) { $browser->visit('/') ->within(new DatePicker, function (Browser $browser) { $browser->selectDate(2019, 1, 30); }) ->assertSee('January'); }); } }

특정 컴포넌트로 범위(scope)가 한정된 브라우저 인스턴스가 필요하다면 component 메서드를 사용할 수도 있습니다.

$datePicker = $browser->component(new DatePickerComponent); $datePicker->selectDate(2019, 1, 30); $datePicker->assertSee('January');

지속적 통합(CI)

WARNING

대부분의 Dusk CI 설정은 애플리케이션이 8000번 포트에서 PHP 내장 개발 서버로 서비스되는 것을 전제로 합니다. 따라서 CI 환경을 구성하기 전에 APP_URL 환경 변수 값이 http://127.0.0.1:8000으로 설정되어 있는지 반드시 확인하세요.

Heroku CI

Heroku CI에서 Dusk 테스트를 실행하려면, Heroku의 app.json 파일에 다음과 같이 Google Chrome 빌드팩과 스크립트를 추가하세요:

{ "environments": { "test": { "buildpacks": [ { "url": "heroku/php" }, { "url": "https://github.com/heroku/heroku-buildpack-chrome-for-testing" } ], "scripts": { "test-setup": "cp .env.testing .env", "test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux --port=9515 > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve --no-reload > /dev/null 2>&1 &' && php artisan dusk" } } } }

Travis CI

Travis CI에서 Dusk 테스트를 실행하려면 아래와 같은 .travis.yml 설정을 사용하세요. Travis CI는 GUI 환경이 아니기 때문에 Chrome 브라우저를 실행하기 위한 추가 설정이 필요합니다. 또한 PHP 내장 웹 서버를 구동하기 위해 php artisan serve 명령어를 사용합니다:

language: php php: - 8.2 addons: chrome: stable install: - cp .env.testing .env - travis_retry composer install --no-interaction --prefer-dist - php artisan key:generate - php artisan dusk:chrome-driver before_script: - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost & - php artisan serve --no-reload & script: - php artisan dusk

GitHub Actions

GitHub Actions로 Dusk 테스트를 실행한다면 아래 설정 파일을 시작점으로 활용할 수 있습니다. Travis CI와 마찬가지로 php artisan serve 명령어로 PHP 내장 웹 서버를 실행합니다:

name: CI on: [push] jobs: dusk-php: runs-on: ubuntu-latest env: APP_URL: "http://127.0.0.1:8000" DB_USERNAME: root DB_PASSWORD: root MAIL_MAILER: log steps: - uses: actions/checkout@v5 - name: Prepare The Environment run: cp .env.example .env - name: Create Database run: | sudo systemctl start mysql mysql --user="root" --password="root" -e "CREATE DATABASE \`my-database\` character set UTF8mb4 collate utf8mb4_bin;" - name: Install Composer Dependencies run: composer install --no-progress --prefer-dist --optimize-autoloader - name: Generate Application Key run: php artisan key:generate - name: Upgrade Chrome Driver run: php artisan dusk:chrome-driver --detect - name: Start Chrome Driver run: ./vendor/laravel/dusk/bin/chromedriver-linux --port=9515 & - name: Run Laravel Server run: php artisan serve --no-reload & - name: Run Dusk Tests run: php artisan dusk - name: Upload Screenshots if: failure() uses: actions/upload-artifact@v4 with: name: screenshots path: tests/Browser/screenshots - name: Upload Console Logs if: failure() uses: actions/upload-artifact@v4 with: name: console path: tests/Browser/console

Chipper CI

Chipper CI로 Dusk 테스트를 실행한다면 아래 설정 파일을 시작점으로 삼을 수 있습니다. 여기서도 요청을 처리하기 위해 PHP 내장 서버로 Laravel을 실행합니다:

# file .chipperci.yml version: 1 environment: php: 8.2 node: 16 # 빌드 환경에 Chrome을 포함시킵니다 services: - dusk # 모든 커밋을 빌드합니다 on: push: branches: .* pipeline: - name: Setup cmd: | cp -v .env.example .env composer install --no-interaction --prefer-dist --optimize-autoloader php artisan key:generate # dusk 전용 env 파일을 생성하며, APP_URL이 BUILD_HOST를 사용하도록 설정합니다 cp -v .env .env.dusk.ci sed -i "s@APP_URL=.*@APP_URL=http://$BUILD_HOST:8000@g" .env.dusk.ci - name: Compile Assets cmd: | npm ci --no-audit npm run build - name: Browser Tests cmd: | php -S [::0]:8000 -t public 2>server.log & sleep 2 php artisan dusk:chrome-driver $CHROME_DRIVER php artisan dusk --env=ci

NOTE

데이터베이스 연동 방법을 포함해 Chipper CI에서 Dusk 테스트를 실행하는 방법에 대한 더 자세한 내용은 Chipper CI 공식 문서를 참고하세요.

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 9월 17일