Laravel Dusk cung cấp một cách kiểm thử API và tự động hóa trình duyệt một cách nhanh chóng và dễ sử dụng. Mặc định, Dusk không yêu cầu bạn phải cài đặt một JDK hoặc Selenium nào trên máy của bạn. Thay vào đó, Dusk sử dụng một cài đặt độc lập ChromeDriver. Tuy nhiên, bạn có thể tự do sử dụng bất kỳ driver nào tương thích với Selenium mà bạn muốn.
Để bắt đầu, bạn cần thêm library laravel/dusk
cho Composer trong project của bạn:
composer require --dev laravel/dusk
{note} Nếu bạn đang đăng ký thủ công service provider của Dusk, thì bạn đừng bao giờ đăng ký nó trong môi trường production của bạn, vì làm như vậy sẽ có thể dẫn đến bất kỳ người dùng nào cũng có thể được authenticate vào application của bạn.
Sau khi cài đặt package Dusk, hãy chạy lệnh Artisan dusk:install
:
php artisan dusk:install
Một thư mục Browser
sẽ được tạo trong thư mục tests
và sẽ chứa một bài test mẫu. Tiếp theo, cài đặt biến môi trường APP_URL
trong file .env
của bạn. Giá trị này phải giống với giá trị URL mà bạn đang sử dụng để truy cập vào application của bạn trên trình duyệt.
Để chạy các bài test của bạn, hãy sử dụng lệnh Artisan dusk
. Lệnh dusk
chấp nhận tất cả các tham số mà lệnh phpunit
chấp nhận:
php artisan dusk
Khi bạn chạy lệnh dusk
, nếu bạn gặp lỗi ở chỗ cuối cùng, thì bạn có thể tiết kiệm thời gian bằng cách chạy lại chỗ lỗi cuối cùng đó trước bằng lệnh dusk:fails
:
php artisan dusk:fails
Nếu bạn muốn cài đặt một phiên bản ChromeDriver khác, khác với phiên bản đi kèm trong Laravel Dusk, bạn có thể sử dụng lệnh dusk:chrome-driver
:
# Install the latest version of ChromeDriver for your OS...
php artisan dusk:chrome-driver
# Install a given version of ChromeDriver for your OS...
php artisan dusk:chrome-driver 74
# Install a given version of ChromeDriver for all supported OSs...
php artisan dusk:chrome-driver --all
{note} Dusk sẽ yêu cầu file
chromedriver
của nó phải có quyền chạy. Nếu như bạn đang gặp lỗi khi chạy Dusk, thì bạn nên đảm bảo là file đó đã có quyền chạy bằng lệnh sau:chmod -R 0755 vendor/laravel/dusk/bin/
.
Mặc định, Dusk sử dụng Google Chrome và cài đặt ChromeDriver để chạy các bài test browser của bạn. Tuy nhiên, bạn có thể khởi động một server Selenium riêng và chạy bài test đó với bất kỳ browser nào mà bạn muốn.
Để bắt đầu, hãy mở file tests/DuskTestCase.php
, đây là file test case cơ bản của Dusk cho application của bạn. Trong file này, bạn có thể xoá dòng gọi đến phương thức startChromeDriver
. Điều này sẽ ngăn Dusk tự động khởi động ChromeDriver:
/**
* Prepare for Dusk test execution.
*
* @beforeClass
* @return void
*/
public static function prepare()
{
// static::startChromeDriver();
}
Tiếp theo, bạn cần phải sửa phương thức driver
để kết nối tới URL và cổng mà bạn chọn. Ngoài ra, bạn cũng có thể sửa "các thông số cho trình duyệt" mà bạn muốn truyền đến WebDriver:
/**
* Create the RemoteWebDriver instance.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
return RemoteWebDriver::create(
'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs()
);
}
Để tạo một bài test Dusk, hãy sử dụng lệnh Artisan dusk:make
. Bài test sẽ được tạo và nằm trong thư mục tests/Browser
:
php artisan dusk:make LoginTest
Để chạy test browser của bạn, hãy sử dụng lệnh Artisan dusk
:
php artisan dusk
Khi bạn chạy lệnh dusk
, nếu bạn gặp lỗi ở chỗ cuối cùng, thì bạn có thể tiết kiệm thời gian bằng cách chạy lại chỗ lỗi cuối cùng đó trước bằng lệnh dusk:fails
:
php artisan dusk:fails
Lệnh dusk
chấp nhận tất cả các tham số mà PHPUnit test chấp nhận, cho phép bạn chỉ chạy các bài test cho một group nhất định, vv...:
php artisan dusk --group=foo
Mặc định, Dusk sẽ thử khởi động ChromeDriver. Nếu ChromeDriver không hoạt động trong hệ thống của bạn, bạn phải tự khởi động nó trước khi chạy lệnh dusk
. Nếu bạn muốn tự khởi động ChromeDriver, bạn nên comment out dòng lệnh sau trong file tests/DuskTestCase.php
của bạn:
/**
* Prepare for Dusk test execution.
*
* @beforeClass
* @return void
*/
public static function prepare()
{
// static::startChromeDriver();
}
Ngoài ra, nếu bạn khởi động ChromeDriver trên một cổng khác, ví dụ như là 9515, bạn nên sửa phương thức driver
trong một class đó:
/**
* Create the RemoteWebDriver instance.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()
);
}
Để bắt buộc Dusk phải sử dụng file môi trường của chính nó khi chạy test, hãy tạo file .env.dusk.{environment}
trong thư mục root của project của bạn. Ví dụ, nếu bạn chạy lệnh dusk
từ môi trường local
, bạn hãy tạo một file .env.dusk.local
.
Khi chạy test, Dusk sẽ back-up file .env
gốc của bạn và đổi tên file môi trường của Dusk thành file .env
. Khi các bài test đã được chạy xong, file .env
của bạn sẽ được khôi phục.
Để bắt đầu, hãy viết một bài test để kiểm tra xem chúng ta có thể đăng nhập vào ứng dụng của chúng ta hay không. Sau khi đã tạo xong bài test, chúng ta có thể sửa nó để điều hướng đến trang đăng nhập, và sau đó nhập một số thông tin đăng nhập và nhấp vào nút "Đăng nhập". Để tạo một instance browser, hãy gọi phương thức browse
:
<?php
namespace Tests\Browser;
use App\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
/**
* A basic browser test example.
*
* @return void
*/
public function testBasicExample()
{
$user = factory(User::class)->create([
'email' => '[email protected]',
]);
$this->browse(function ($browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'password')
->press('Login')
->assertPathIs('/home');
});
}
}
Như bạn có thể thấy trong ví dụ trên, phương thức browse
chấp nhận một callback. Một instance browser sẽ tự động được truyền vào callback đó và nó là đối tượng chính để tương tác và đưa ra các yêu cầu đối với application của bạn.
Thỉnh thoảng bạn có thể cần chạy nhiều trình duyệt trong cùng một lúc để thực hiện test. Ví dụ: có thể cần chạy nhiều trình duyệt để kiểm tra một màn hình trò chuyện sử dụng websocket. Để tạo nhiều trình duyệt, hãy khai báo nhiều trình duyệt trong hàm callback được sử dụng cho phương thức browse
:
$this->browse(function ($first, $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');
});
Bạn có thể sử dụng phương thức resize
để điều chỉnh kích thước của browser window:
$browser->resize(1920, 1080);
Phương thức maximize
có thể được sử dụng để set browser window ở chế độ full screen:
$browser->maximize();
Phương thức fitContent
sẽ thay đổi kích thước của browser window để phù hợp với kích thước của nội dung:
$browser->fitContent();
Khi kiểm tra không thành công, Dusk sẽ tự động thay đổi kích thước trình duyệt để phù hợp với nội dung trước khi chụp ảnh màn hình. Bạn có thể tắt tính năng này bằng cách gọi phương thức disableFitOnFailure
trong bài test của bạn:
$browser->disableFitOnFailure();
Nếu bạn muốn định nghĩa một phương thức trình duyệt tùy biến mà bạn có thể sử dụng lại trong nhiều bài test của bạn, bạn có thể sử dụng phương thức macro
trên class Browser
. Thông thường, bạn nên gọi phương thức này từ phương thức boot
của service provider:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Browser;
class DuskServiceProvider extends ServiceProvider
{
/**
* Register the Dusk's browser macros.
*
* @return void
*/
public function boot()
{
Browser::macro('scrollToElement', function ($element = null) {
$this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);");
return $this;
});
}
}
Phương thức macro
chấp nhận một tên làm tham số đầu tiên và một Closure làm tham số thứ hai của nó. Closure của macro sẽ được chạy khi bạn gọi macro dưới dạng một phương thức trên một implementation của Browser
:
$this->browse(function ($browser) use ($user) {
$browser->visit('/pay')
->scrollToElement('#credit-card-details')
->assertSee('Enter Credit Card Details');
});
Thông thường, bạn sẽ cần test các trang mà cần được authentication. Bạn có thể sử dụng phương thức loginAs
của Dusk để tránh phải tương tác với các màn hình đăng nhập trong mỗi lần test. Phương thức loginAs
chấp nhận ID người dùng hoặc một instance model user:
$this->browse(function ($first, $second) {
$first->loginAs(User::find(1))
->visit('/home');
});
{note} Sau khi sử dụng phương thức
loginAs
, session người dùng sẽ được tạo và duy trì cho tất cả các bài test trong file đó.
Khi bài test của bạn yêu cầu migration, như ví dụ bài test authentication mẫu ở trên, bạn không nên sử dụng trait RefreshDatabase
. Trait RefreshDatabase
sẽ tạo ra các database transaction sẽ không áp dụng trên các HTTP request. Thay vào đó, hãy sử dụng trait DatabaseMigrations
:
<?php
namespace Tests\Browser;
use App\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
}
Chọn các CSS selector tốt để tương tác với các element là một trong những phần khó nhất khi viết bài test cho Dusk. Theo thời gian, các thay đổi ở frontend có thể khiến các CSS selector như sau có thể phá vỡ các bài test của bạn:
// HTML...
<button>Login</button>
// Test...
$browser->click('.login-page .container div > button');
Dusk selector cho phép bạn tập trung vào viết các bài test hiệu quả thay vì phải nhớ các CSS selector. Để định nghĩa một selector, hãy thêm thuộc tính dusk
vào element HTML của bạn. Sau đó, thêm tiền tố @
để thao tác với element đó trong bài test cho Dusk của bạn:
// HTML...
<button dusk="login-button">Login</button>
// Test...
$browser->click('@login-button');
Để click vào một link, bạn có thể sử dụng phương thức clickLink
trên instance browser. Phương thức clickLink
sẽ click vào một link mà có nội dung đã cho:
$browser->clickLink($linkText);
{note} Phương thức này tương tác với jQuery. Nếu jQuery không có sẵn trong trang, Dusk sẽ tự động tích hợp nó vào trang để nó có sẵn trong thời gian test.
Dusk cung cấp một số phương thức để tương tác với text được hiển thị, các giá trị và các thuộc tính hiện tại của element ở trên trang. Ví dụ, để lấy một "giá trị" của một element giống với selector đã cho, hãy sử dụng phương thức value
:
// Retrieve the value...
$value = $browser->value('selector');
// Set the value...
$browser->value('selector', 'value');
Phương thức text
có thể được sử dụng để lấy ra text của một element giống với một selector đã cho:
$text = $browser->text('selector');
Cuối cùng, phương thức attribute
cũng có thể được sử dụng để lấy ra một thuộc tính của một element giống với một selector đã cho:
$attribute = $browser->attribute('selector', 'value');
Dusk cung cấp nhiều phương thức để tương tác với các form và các element input. Trước tiên, hãy xem một ví dụ về cách nhập text vào field input:
$browser->type('email', '[email protected]');
Lưu ý rằng, phương thức này chấp nhận một tham số nếu cần, chúng ta không bắt buộc phải truyền vào một CSS selector cho phương thức type
. Nếu CSS selector không được cung cấp, Dusk sẽ tìm kiếm field input có thuộc tính name
. Cuối cùng, Dusk sẽ thử tìm một textarea
có thuộc tínhname
.
Để nối text vào một field mà không xóa nội dung của nó đi, bạn có thể sử dụng phương thức append
:
$browser->type('tags', 'foo')
->append('tags', ', bar, baz');
Bạn có thể xóa giá trị của một input bằng phương thức clear
:
$browser->clear('email');
Để select một giá trị trong một dropdown selection box, bạn có thể sử dụng phương thức select
. Giống như phương thức type
, phương thứcselect
không yêu cầu một CSS selector đầy đủ. Khi truyền một giá trị cho phương thức select
, bạn nên truyền giá trị tùy chọn bên dưới thay vì text:
$browser->select('size', 'Large');
Bạn có thể select một option ngẫu nhiên bằng cách bỏ qua tham số thứ hai:
$browser->select('size');
Để "check" vào một checkbox, bạn có thể sử dụng phương thức check
. Giống như nhiều phương thức liên quan đến input khác, bạn không cần phải có CSS selector đầy đủ. Nếu không thể tìm thấy chính xác selector, Dusk sẽ tìm kiếm một checkbox có thuộc tính name
:
$browser->check('terms');
$browser->uncheck('terms');
Để "chọn" một radio button, bạn có thể sử dụng phương thức radio
. Giống như nhiều phương thức liên quan đến input khác, bạn không cần phải có CSS selector đầy đủ. Nếu không thể tìm thấy chính xác selector, Dusk sẽ tìm kiếm một radio có thuộc tính name
và value
:
$browser->radio('version', 'php7');
Phương thức attach
có thể được sử dụng để đính kèm một file vào một element input file
. Giống như nhiều phương thức liên quan đến input khác, bạn không cần phải có CSS selector đầy đủ. Nếu không thể tìm thấy selector chính xác, Dusk sẽ tìm kiếm một input file mà có thuộc tính name
:
$browser->attach('photo', __DIR__.'/photos/me.png');
{note} Chức năng đính kèm sẽ yêu cầu bạn cài đặt và enable PHP extension
Zip
trong server của bạn.
Phương thức keys
cho phép bạn cung cấp các chuỗi input phức tạp hơn cho một element so với phương thứctype
cho phép. Ví dụ: bạn có thể giữ các phím chức năng để nhập các giá trị. Trong ví dụ này, phím shift
sẽ được giữ trong khi taylor
sẽ được nhập vào element giống với selector. Sau khi taylor
đã được gõ xong, otwell
sẽ được gõ mà không cần ấn giữ bất kỳ phím nào khác:
$browser->keys('selector', ['{shift}', 'taylor'], 'otwell');
Bạn thậm chí có thể gửi một "hot key" tới CSS selector chính chứa application của bạn:
$browser->keys('.app', ['{command}', 'j']);
{tip} Tất cả các modifier key được bao trong các ký tự
{}
đều giống với các hằng số đã được định nghĩa trong classFacebook\WebDriver\WebDriverKeys
, bạn có thể tìm thấy nó trên GitHub.
Phương thức click
có thể được sử dụng để "click" vào một element giống với một selector đã cho:
$browser->click('.selector');
Phương thức mouseover
có thể được sử dụng khi bạn cần di chuyển chuột qua một element giống với một selector đã cho:
$browser->mouseover('.selector');
Phương thức drag
có thể được sử dụng để kéo một element giống với một selector đã cho sang element khác:
$browser->drag('.from-selector', '.to-selector');
Hoặc, bạn có thể kéo một element theo một hướng:
$browser->dragLeft('.selector', 10);
$browser->dragRight('.selector', 10);
$browser->dragUp('.selector', 10);
$browser->dragDown('.selector', 10);
Dusk cung cấp nhiều phương thức khác nhau để tương tác với JavaScript Dialog:
// Wait for a dialog to appear:
$browser->waitForDialog($seconds = null);
// Assert that a dialog has been displayed and that its message matches the given value:
$browser->assertDialogOpened('value');
// Type the given value in an open JavaScript prompt dialog:
$browser->typeInDialog('Hello World');
Để đóng một JavaScript Dialog đang được mở và nhấp vào nút OK:
$browser->acceptDialog();
Để đóng một JavaScript Dialog đang được mở và nhấp vào nút Cancel (chỉ dành cho dialog confirm):
$browser->dismissDialog();
Đôi khi bạn có thể muốn thực hiện một số thao tác trong một phạm vi selector đã cho. Ví dụ: bạn có thể muốn kiểm tra rằng có một số text chỉ được hiển thị trong một bảng và sau đó click vào một button trong bảng đó. Bạn có thể sử dụng phương thức with
để thực hiện điều này. Tất cả các hoạt động được thực hiện trong hàm callback được đưa vào trong phương thức with
và sẽ được thực hiện test trong phạm vi của selector đã chọn:
$browser->with('.table', function ($table) {
$table->assertSee('Hello World')
->clickLink('Delete');
});
Khi test các application sử dụng JavaScript chuyên sâu, thường phải "chờ" các element hoặc dữ liệu được load xong trước khi tiến hành test. Dusk thực hiện điều này một cách rất dễ dàng. Sử dụng nhiều phương thức khác nhau, bạn có thể đợi các element hiển thị hoàn toàn trên trang hoặc thậm chí đợi cho đến khi một biểu thức JavaScript trả về kết quả là true
.
Nếu bạn cần pause bài test trong một số mili giây nhất định, hãy sử dụng phương thức pause
:
$browser->pause(1000);
Phương thức waitFor
có thể được sử dụng để tạm dừng việc test cho đến khi element mà khớp với CSS selector được hiển thị trên trang. Mặc định, điều này sẽ tạm dừng bài test trong tối đa năm giây trước khi đưa ra một ngoại lệ. Nếu cần, bạn có thể truyền vào một ngưỡng thời gian chờ để làm tham số thứ hai cho phương thức:
// Wait a maximum of five seconds for the selector...
$browser->waitFor('.selector');
// Wait a maximum of one second for the selector...
$browser->waitFor('.selector', 1);
Bạn cũng có thể đợi cho đến khi một selector của bạn bị ẩn đi khỏi trang:
$browser->waitUntilMissing('.selector');
$browser->waitUntilMissing('.selector', 1);
Đôi khi, bạn có thể muốn đợi một selector nhất định và sau đó tương tác với element khớp với selector đó. Ví dụ, bạn có thể đợi cho đến khi một modal window được hiển thị và sau đó nhấn nút "OK" trong modal đó. Phương thức whenAvailable
có thể được sử dụng cho trường hợp này. Tất cả các hoạt động của element được thực hiện trong callback sẽ nằm trong phạm vi của selector ban đầu:
$browser->whenAvailable('.modal', function ($modal) {
$modal->assertSee('Hello World')
->press('OK');
});
Phương thức waitForText
có thể được sử dụng để đợi cho đến khi một text được hiển thị trên trang:
// Wait a maximum of five seconds for the text...
$browser->waitForText('Hello World');
// Wait a maximum of one second for the text...
$browser->waitForText('Hello World', 1);
Bạn có thể sử dụng phương thức waitUntilMissingText
để đợi cho đến khi văn bản đang được hiển thị bị xóa khỏi trang:
// Wait a maximum of five seconds for the text to be removed...
$browser->waitUntilMissingText('Hello World');
// Wait a maximum of one second for the text to be removed...
$browser->waitUntilMissingText('Hello World', 1);
Phương thức waitForLink
có thể được sử dụng để đợi cho đến khi một link đã cho được hiển thị trên trang:
// Wait a maximum of five seconds for the link...
$browser->waitForLink('Create');
// Wait a maximum of one second for the link...
$browser->waitForLink('Create', 1);
Khi thực hiện kiểm tra đường dẫn, chẳng hạn như $browser->assertPathIs('/home')
, kiểm tra đó có thể thất bại nếu window.location.pathname
không đồng bộ với nhau. Bạn có thể sử dụng phương thức waitForLocation
để đợi cho đến khi window.location là một giá trị nhất định:
$browser->waitForLocation('/secret');
Bạn cũng có thể đợi location của một route đã được đặt tên:
$browser->waitForRoute($routeName, $parameters);
Nếu bạn cần thực hiện các kiểm tra sau khi một trang đã được reload, hãy sử dụng phương thức waitForReload
:
$browser->click('.some-action')
->waitForReload()
->assertSee('something');
Thỉnh thoảng bạn có thể muốn tạm dừng việc kiểm tra cho đến khi một biểu thức JavaScript trả về giá trị là true
. Bạn có thể dễ dàng thực hiện điều này bằng cách sử dụng phương thức waitUntil
. Khi truyền một biểu thức cho phương thức này, bạn không cần thêm từ khóa return
hoặc dấu chấm phẩy kết thúc:
// Wait a maximum of five seconds for the expression to be true...
$browser->waitUntil('App.dataLoaded');
$browser->waitUntil('App.data.servers.length > 0');
// Wait a maximum of one second for the expression to be true...
$browser->waitUntil('App.data.servers.length > 0', 1);
Các phương thức sau có thể được sử dụng để đợi cho đến khi một thuộc tính Vue component có một giá trị nhất định:
// Wait until the component attribute contains the given value...
$browser->waitUntilVue('user.name', 'Taylor', '@user');
// Wait until the component attribute doesn't contain the given value...
$browser->waitUntilVueIsNot('user.name', null, '@user');
Nhiều phương thức "chờ" trong Dusk được dựa trên phương thức waitUsing
bên dưới. Bạn có thể sử dụng phương thức này trực tiếp để chờ cho đến khi một callback trả về giá trị true
. Phương thức waitUsing
nhận vào số giây chờ tối đa mà bài test có thể được thực hiện và một khoảng thời gian lặp cho Closure và một Closure và một tuỳ chọn thông báo lỗi:
$browser->waitUsing(10, 1, function () use ($something) {
return $something->isReady();
}, "Something wasn't ready in time.");
Dusk thậm chí cho phép bạn thực hiện các kiểm tra về trạng thái dữ liệu của Vue component. Ví dụ: hãy giả sử application của bạn chứa một Vue component sau:
// HTML...
<profile dusk="profile-component"></profile>
// Component Definition...
Vue.component('profile', {
template: '<div>{{ user.name }}</div>',
data: function () {
return {
user: {
name: 'Taylor'
}
};
}
});
Bạn có thể kiểm tra trạng thái của Vue component như sau:
/**
* A basic Vue test example.
*
* @return void
*/
public function testVue()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertVue('user.name', 'Taylor', '@profile-component');
});
}
Dusk cung cấp nhiều yêu cầu kiểm tra mà bạn có thể đưa ra đối với ứng dụng của bạn. Tất cả các yêu cầu kiểm tra có sẵn đều được ghi lại trong danh sách dưới đây:
assertTitle assertTitleContains assertUrlIs assertSchemeIs assertSchemeIsNot assertHostIs assertHostIsNot assertPortIs assertPortIsNot assertPathBeginsWith assertPathIs assertPathIsNot assertRouteIs assertQueryStringHas assertQueryStringMissing assertFragmentIs assertFragmentBeginsWith assertFragmentIsNot assertHasCookie assertCookieMissing assertCookieValue assertPlainCookieValue assertSee assertDontSee assertSeeIn assertDontSeeIn assertSourceHas assertSourceMissing assertSeeLink assertDontSeeLink assertInputValue assertInputValueIsNot assertChecked assertNotChecked assertRadioSelected assertRadioNotSelected assertSelected assertNotSelected assertSelectHasOptions assertSelectMissingOptions assertSelectHasOption assertValue assertVisible assertPresent assertMissing assertDialogOpened assertEnabled assertDisabled assertButtonEnabled assertButtonDisabled assertFocused assertNotFocused assertVue assertVueIsNot assertVueContains assertVueDoesNotContain
Yêu cầu title của page phải đúng với text đã cho:
$browser->assertTitle($title);
Yêu cầu title của page phải chứa text đã cho:
$browser->assertTitleContains($title);
Yêu cầu URL hiện tại (bỏ phần query string) phải đúng với chuỗi đã cho:
$browser->assertUrlIs($url);
Yêu cầu scheme của URL hiện tại phải đúng với scheme đã cho:
$browser->assertSchemeIs($scheme);
Yêu cầu scheme của URL hiện tại không phải scheme đã cho:
$browser->assertSchemeIsNot($scheme);
Yêu cầu host của URL hiện tại phải đúng với host đã cho:
$browser->assertHostIs($host);
Yêu cầu host của URL hiện tại không phải host đã cho:
$browser->assertHostIsNot($host);
Yêu cầu port của URL hiện tại phải đúng với port đã cho:
$browser->assertPortIs($port);
Yêu cầu port của URL hiện tại không phải port đã cho:
$browser->assertPortIsNot($port);
Yêu cầu path của URL hiện tại phải bắt đầu từ path đã cho:
$browser->assertPathBeginsWith($path);
Yêu cầu path hiện tại phải đúng với path đã cho:
$browser->assertPathIs('/home');
Yêu cầu path hiện tại không phải là path đã cho:
$browser->assertPathIsNot('/home');
Yêu cầu URL hiện tại phải đúng với URL của một route đã cho:
$browser->assertRouteIs($name, $parameters);
Yêu cầu tham số query string phải tồn tại:
$browser->assertQueryStringHas($name);
Yêu cầu tham số query string phải tồn tại và có giá trị đã cho:
$browser->assertQueryStringHas($name, $value);
Yêu cầu tham số query string bị thiếu:
$browser->assertQueryStringMissing($name);
Yêu cầu fragment hiệnt tại phải đúng với fragment đã cho:
$browser->assertFragmentIs('anchor');
Yêu cầu fragment hiệnt tại phải bắt đầu từ fragment đã cho:
$browser->assertFragmentBeginsWith('anchor');
Yêu cầu fragment hiệnt tại không phải là fragment đã cho:
$browser->assertFragmentIsNot('anchor');
Yêu cầu cookie đã cho phải tồn tại:
$browser->assertHasCookie($name);
Yêu cầu cookie đã cho phải không tồn tại:
$browser->assertCookieMissing($name);
Yêu cầu cookie phải có một giá trị đã cho:
$browser->assertCookieValue($name, $value);
Yêu cầu một cookie chưa được mã hóa có một giá trị nhất định:
$browser->assertPlainCookieValue($name, $value);
Yêu cầu text đã cho có trong page:
$browser->assertSee($text);
Yêu cầu text đã cho không có trong page:
$browser->assertDontSee($text);
Yêu cầu text đã cho phải có trong selector:
$browser->assertSeeIn($selector, $text);
Yêu cầu text đã cho không có trong selector:
$browser->assertDontSeeIn($selector, $text);
Yêu cầu source code đã cho có trong page:
$browser->assertSourceHas($code);
Yêu cầu source code đã cho không có trong page:
$browser->assertSourceMissing($code);
Yêu cầu link đã cho có trong page:
$browser->assertSeeLink($linkText);
Yêu cầu link đã cho không có trong page:
$browser->assertDontSeeLink($linkText);
Yêu cầu input field phải có giá trị đã cho:
$browser->assertInputValue($field, $value);
Yêu cầu input field phải không có giá trị đã cho:
$browser->assertInputValueIsNot($field, $value);
Yêu cầu checkbox phải được chọn:
$browser->assertChecked($field);
Yêu cầu checkbox không được chọn:
$browser->assertNotChecked($field);
Yêu cầu radio phải chọn giá trị đã cho:
$browser->assertRadioSelected($field, $value);
Yêu cầu radio không được chọn giá trị đã cho:
$browser->assertRadioNotSelected($field, $value);
Yêu cầu dropdown phải chọn giá trị đã cho:
$browser->assertSelected($field, $value);
Yêu cầu dropdown không được chọn giá trị đã cho:
$browser->assertNotSelected($field, $value);
Yêu cầu một mảng giá trị có thể được chọn:
$browser->assertSelectHasOptions($field, $values);
Yêu cầu một mảng giá trị không thể được chọn:
$browser->assertSelectMissingOptions($field, $values);
Yêu cầu một giá trị có thể được chọn trên một field:
$browser->assertSelectHasOption($field, $value);
Yêu cầu element giống với selector đã cho:
$browser->assertValue($selector, $value);
Yêu cầu element giống với selector đã cho là hiển thị:
$browser->assertVisible($selector);
Yêu cầu element giống với selector đã cho là tồn tại:
$browser->assertPresent($selector);
Yêu cầu element giống với selector đã cho là không hiển thị:
$browser->assertMissing($selector);
Yêu cầu một dialog JavaScript cũng với message đã cho đang được hiển thị:
$browser->assertDialogOpened($message);
Yêu cầu field đã cho đang được enabled:
$browser->assertEnabled($field);
Yêu cầu field đã cho đang bị disable:
$browser->assertDisabled($field);
Yêu cầu button đã cho đang được enabled:
$browser->assertButtonEnabled($button);
Yêu cầu button đã cho đang bị disable:
$browser->assertButtonDisabled($button);
Yêu cầu field đã cho đang bị focus:
$browser->assertFocused($field);
Yêu cầu field đã cho không bị focus:
$browser->assertNotFocused($field);
Yêu cầu một thuộc tính dữ liệu của Vue component giống với giá trị đã cho:
$browser->assertVue($property, $value, $componentSelector = null);
Yêu cầu một thuộc tính dữ liệu của Vue component khác với giá trị đã cho:
$browser->assertVueIsNot($property, $value, $componentSelector = null);
Yêu cầu một thuộc tính dữ liệu của Vue component là một mảng và có chứa giá trị đã cho:
$browser->assertVueContains($property, $value, $componentSelector = null);
Yêu cầu một thuộc tính dữ liệu của Vue component là một mảng và không chứa giá trị đã cho:
$browser->assertVueDoesNotContain($property, $value, $componentSelector = null);
Đôi khi, các bài test yêu cầu một số hành động phức tạp được thực hiện theo một trình tự nhất định. Điều này có thể làm cho bài test của bạn khó đọc hiểu hơn. Page cho phép bạn định nghĩa các hành động có thể được thực hiện trên một trang nhất định bằng một phương thức duy nhất. Page cũng cho phép bạn định nghĩa các short-cut cho các common selector trong application của bạn hoặc trong một trang.
Để tạo một page object, sử dụng lệnh Artisan dusk:page
. Tất cả các page object sẽ được lưu trong thư mục tests/Browser/Pages
:
php artisan dusk:page Login
Mặc định, các page có ba phương thức: url
, assert
, và elements
. Bây giờ chúng ta sẽ thảo luận về các phương thức url
và assert
. Phương thức elements
sẽ được thảo luận chi tiết hơn bên dưới.
url
MethodPhương thức url
sẽ trả về đường dẫn của URL đến một trang. Dusk sẽ sử dụng URL này khi điều hướng đến trang đó trong trình duyệt:
/**
* Get the URL for the page.
*
* @return string
*/
public function url()
{
return '/login';
}
assert
MethodPhương thức assert
có thể đưa ra bất kỳ yêu cầu nào cần thiết để kiểm tra là trình duyệt đã thực sự mở trang đó hay chưa. Hoàn thành phương thức này là không cần thiết; tuy nhiên, bạn có thể tự do đưa ra những yêu cầu này nếu muốn. Các yêu cầu này sẽ được chạy tự động khi mở trang đó:
/**
* Assert that the browser is on the page.
*
* @return void
*/
public function assert(Browser $browser)
{
$browser->assertPathIs($this->url());
}
Khi một page đã được cấu hình, bạn có thể điều hướng đến nó bằng phương thức visit
:
use Tests\Browser\Pages\Login;
$browser->visit(new Login);
Thỉnh thoảng bạn có thể đã ở trên một trang và cần "load" lại các selector và phương thức của trang đó vào test hiện tại. Điều này rất phổ biến khi bạn nhấn một nút và được chuyển hướng đến một trang khác mà không điều hướng đến nó. Trong tình huống này, bạn có thể sử dụng phương thức on
để load trang:
use Tests\Browser\Pages\CreatePlaylist;
$browser->visit('/dashboard')
->clickLink('Create Playlist')
->on(new CreatePlaylist)
->assertSee('@create');
Phương thức elements
của trang cho phép bạn định nghĩa các shortcut nhanh, dễ nhớ cho bất kỳ CSS selector nào trên trang của bạn. Ví dụ: hãy định nghĩa shortcut cho field input "email" trong trang đăng nhập của application:
/**
* Get the element shortcuts for the page.
*
* @return array
*/
public function elements()
{
return [
'@email' => 'input[name=email]',
];
}
Bây giờ, bạn có thể sử dụng shorthand selector này ở bất cứ nơi nào mà bạn dự kiến sử dụng CSS selector đó:
$browser->type('@email', '[email protected]');
Sau khi cài đặt Dusk, một class Page
sẽ được lưu vào trong thư mục tests/Browser/Pages
của bạn. Class này chứa một phương thức siteElements
có thể được sử dụng để định nghĩa các global shorthand selector mà sẽ được khai báo sẵn trên mỗi trang trong application của bạn:
/**
* Get the global element shortcuts for the site.
*
* @return array
*/
public static function siteElements()
{
return [
'@element' => '#selector',
];
}
Ngoài các phương thức mặc định được định nghĩa trên các trang, bạn có thể định nghĩa thêm các phương thức có thể được sử dụng trong suốt qua trình test của bạn. Ví dụ: hãy giả sử rằng chúng ta đang xây dựng một application quản lý nhạc. Một hành động chung cho một trang của application có thể là tạo một playlist. Thay vì viết lại logic để tạo playlist cho mỗi bài test, bạn có thể định nghĩa phương thức createPlaylist
trên một class page:
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
class Dashboard extends Page
{
// Other page methods...
/**
* Create a new playlist.
*
* @param \Laravel\Dusk\Browser $browser
* @param string $name
* @return void
*/
public function createPlaylist(Browser $browser, $name)
{
$browser->type('name', $name)
->check('share')
->press('Create Playlist');
}
}
Khi phương thức đã được định nghĩa xong, bạn có thể sử dụng nó trong bất kỳ bài test nào mà bạn đang sử dụng page đó. Instance browser sẽ được tự động chuyển đến phương thức của page đó:
use Tests\Browser\Pages\Dashboard;
$browser->visit(new Dashboard)
->createPlaylist('My Playlist')
->assertSee('My Playlist');
Các component cũng tương tự như các đối tượng page của Dusk, nhưng được dành cho các thành phần của UI và các chức năng được sử dụng lại trong toàn bộ application của bạn, như một navigation bar hoặc một notification window. Như vậy, các component không bị ràng buộc với các URL cụ thể.
Để tạo một component, bạn hãy sử dụng lệnh Artisan dusk:component
. Các component mới sẽ được lưu vào trong thư mục tests/Browser/Components
:
php artisan dusk:component DatePicker
Như câu lệnh ở trên, một "date picker" có thể là một ví dụ mẫu cho một component tồn tại trong toàn bộ application của bạn và trong nhiều trang khác nhau. Nó sẽ rất cồng kềnh nếu viết các logic của browser để chọn một ngày cho hàng chục bài test có trong test suite của bạn. Thay vào đó, chúng ta có thể định nghĩa một component Dusk để đại diện cho date picker, cho phép chúng ta gói gọn các logic đó vào trong một component:
<?php
namespace Tests\Browser\Components;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Component as BaseComponent;
class DatePicker extends BaseComponent
{
/**
* Get the root selector for the component.
*
* @return string
*/
public function selector()
{
return '.date-picker';
}
/**
* Assert that the browser page contains the component.
*
* @param Browser $browser
* @return void
*/
public function assert(Browser $browser)
{
$browser->assertVisible($this->selector());
}
/**
* Get the element shortcuts for the component.
*
* @return array
*/
public function elements()
{
return [
'@date-field' => 'input.datepicker-input',
'@year-list' => 'div > div.datepicker-years',
'@month-list' => 'div > div.datepicker-months',
'@day-list' => 'div > div.datepicker-days',
];
}
/**
* Select the given date.
*
* @param \Laravel\Dusk\Browser $browser
* @param int $year
* @param int $month
* @param int $day
* @return void
*/
public function selectDate($browser, $year, $month, $day)
{
$browser->click('@date-field')
->within('@year-list', function ($browser) use ($year) {
$browser->click($year);
})
->within('@month-list', function ($browser) use ($month) {
$browser->click($month);
})
->within('@day-list', function ($browser) use ($day) {
$browser->click($day);
});
}
}
Khi component đã được định nghĩa xong, chúng ta có thể dễ dàng chọn một ngày trong date picker, từ bất kỳ bài test nào. Và, nếu chúng ta cần thay đổi logic chọn ngày, chúng ta chỉ cần cập nhật component:
<?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
{
/**
* A basic component test example.
*
* @return void
*/
public function testBasicExample()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->within(new DatePicker, function ($browser) {
$browser->selectDate(2019, 1, 30);
})
->assertSee('January');
});
}
}
{note} Trước khi thêm file cấu hình test tích hợp, hãy đảm bảo rằng file
.env.testing
của bạn đã chứa biếnAPP_URL
có giá trị làhttp://127.0.0.1:8000
.
Nếu bạn đang sử dụng CircleCI để chạy các bài test, bạn có thể sử dụng file cấu hình này làm file khởi đầu. Giống như TravisCI, chúng ta sẽ sử dụng lệnh php artisan serve
để khởi chạy web server của PHP:
version: 2
jobs:
build:
steps:
- run: sudo apt-get install -y libsqlite3-dev
- run: cp .env.testing .env
- run: composer install -n --ignore-platform-reqs
- run: php artisan key:generate
- run: php artisan dusk:chrome-driver
- run: npm install
- run: npm run production
- run: vendor/bin/phpunit
- run:
name: Start Chrome Driver
command: ./vendor/laravel/dusk/bin/chromedriver-linux
background: true
- run:
name: Run Laravel Server
command: php artisan serve
background: true
- run:
name: Run Laravel Dusk Tests
command: php artisan dusk
- store_artifacts:
path: tests/Browser/screenshots
Để chạy các bài test trên Codeship, hãy thêm các lệnh sau vào Codeship project của bạn. Các lệnh này là các lệnh cơ bản và bạn có thể tự do thêm các lệnh khác khi cần:
phpenv local 7.2
cp .env.testing .env
mkdir -p ./bootstrap/cache
composer install --no-interaction --prefer-dist
php artisan key:generate
php artisan dusk:chrome-driver
nohup bash -c "php artisan serve 2>&1 &" && sleep 5
php artisan dusk
Để chạy các bài Dusk test trên Heroku CI, hãy thêm gói và tập lệnh sau của Google Chrome vào file Heroku app.json
của bạn:
{
"environments": {
"test": {
"buildpacks": [
{ "url": "heroku/php" },
{ "url": "https://github.com/heroku/heroku-buildpack-google-chrome" }
],
"scripts": {
"test-setup": "cp .env.testing .env",
"test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve > /dev/null 2>&1 &' && php artisan dusk"
}
}
}
}
Để chạy các bài Dusk test của bạn trên Travis CI, bạn có thể dùng file cấu hình .travis.yml
sau. Vì Travis CI không phải là một môi trường đồ họa, nên chúng ta sẽ cần thực hiện thêm một số bước để chạy trình duyệt Chrome. Ngoài ra, chúng ta cũng sẽ sử dụng php artisan serve
để chạy server web tích hợp sẵn của PHP:
language: php
php:
- 7.3
addons:
chrome: stable
install:
- cp .env.testing .env
- travis_retry composer install --no-interaction --prefer-dist --no-suggest
- 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 &
script:
- php artisan dusk
Nếu bạn đang sử dụng Github Actions để chạy các bài test Laravel Dusk, bạn có thể sử dụng file cấu hình này để bắt đầu. Giống như TravisCI, chúng ta sẽ sử dụng lệnh php artisan serve
để khởi chạy một web server được tích hợp sẵn trong PHP:
name: CI
on: [push]
jobs:
dusk-php:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Prepare The Environment
run: cp .env.example .env
- name: Create Database
run: 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 --no-suggest --prefer-dist --optimize-autoloader
- name: Generate Application Key
run: php artisan key:generate
- name: Upgrade Chrome Driver
run: php artisan dusk:chrome-driver
- 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
entry