瀏覽器測試 Dusk
Browser Tests (Laravel Dusk)
Laravel Dusk 提供了富有表現力、簡單易用的瀏覽器自動化及測試API 。預設情況下,Dusk 不需要在你的機器上安裝 JDK 或 Selenium 。而是需要使用單獨的安裝
你應該先在你的Composer 上加入laravel/dusk
# 依賴:
composer require --dev laravel/dusk
{note} 如果你是手動註冊Dusk 服務提供者,一定不能 在你的生產環境中註冊,這可能會導致一些不守規矩的使用者擁有控制你應用的權限。
安裝好Dusk 套件後,執行dusk:install
指令:
php artisan dusk:install
Browser
目錄將會在tests
目錄下被創建,並且包含一個測試案例。接下來,在你的 .env
檔案中設定 APP_URL
變數。這個值應該與你在瀏覽器中開啟本應用程式的 URL 相符。
要執行測試,使用 dusk
指令。 dusk
指令可以使用與phpunit
指令相同的參數:
php artisan dusk
如果上次執行 dusk
指令時測試失敗,可以透過使用dusk:fails
指令重新執行失敗的測試來節省時間:
php artisan dusk:fails
{注意} Dusk 要求 ChromeDriver 二進位檔案是可執行的。如果在 Dusk 運行時遇到問題,可以使用以下命令確保二進位檔案是可執行的:
chmod -R 0755 vendor/laravel/dusk/bin
。
使用其他瀏覽器
預設情況下, Dusk 使用Google Chrome 瀏覽器和一個單獨的ChromeDriver 的安裝來執行你的瀏覽器測試。當然,你可以運行你自己的 Selenium 服務,用任何你想使用的瀏覽器來測試。
如果要這麼做,打開 tests/DuskTestCase.php
文件,這個是應用測試用例的基底類別。在這個檔案中,你可以移除對 startChromeDriver
方法的呼叫。這樣 Dusk 就不會自動啟動 ChromeDriver 了。
/** * 准备执行 Dusk 测试。 * * @beforeClass * @return void */ public static function prepare(){ // static::startChromeDriver(); }
然後,你可以修改 driver
方法來連接到你選定的 URL 和連接埠。此外,你可以修改「desired capabilities」(期望能力),它將會傳遞給WebDriver:
/** * 创建 RemoteWebDriver 实例。 * * @return \Facebook\WebDriver\Remote\RemoteWebDriver */ protected function driver(){ return RemoteWebDriver::create( 'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs() ); }##開始使用建立測試要建立一個Dusk 測試,使用
dusk:make 指令。建立的測試將會放在
tests/Browser 目錄:
php artisan dusk:make LoginTest##
執行測試
使用dusk
指令來執行你的瀏覽器測試:
php artisan dusk
如果上次執行 dusk
指令時測試失敗,則可以透過使用dusk:fails
指令重新執行失敗的測試來節省時間:
php artisan dusk:fails
dusk
指令接受任何能讓PHPUnit 正常運作的參數。例如,讓你可以在指定group 中執行測試:
php artisan dusk --group=foo
#手動執行ChromeDriver
##預設情況下,Dusk 會嘗試自動執行ChromeDriver 。如果你在特定的系統中無法執行,可以在執行dusk 指令前透過手動的方式來執行 ChromeDriver。如果你選擇手動執行ChromeDriver,你需要在你的
tests/DuskTestCase.php 檔案中註解掉下面這一行:
/** * 为 Dusk 测试做准备。 * * @beforeClass * @return void */ public static function prepare(){ // static::startChromeDriver(); }另外,如果你的ChromeDriver 運行在非9515 端口,你需要修改同一個類別中的
driver 方法:
/** * 创建 RemoteWebDriver 实例。 * * @return \Facebook\WebDriver\Remote\RemoteWebDriver */ protected function driver(){ return RemoteWebDriver::create( 'http://localhost:9515', DesiredCapabilities::chrome() ); }環境處理為了讓Dusk 使用自己的環境檔案來執行測試,你需要在專案根目錄中建立一個
.env.dusk.{environment} 檔案。簡單的說,如果你想用
local 環境來執行
dusk 指令,你需要建立一個
.env.dusk.local 檔。
.env 檔案並且重新命名你的 Dusk 環境檔案為
.env。當測試結束後,它會恢復你的
.env 檔案。
browse 方法來建立一個瀏覽器實例:
<?php namespace Tests\Browser; use App\User;use Tests\DuskTestCase; use Laravel\Dusk\Chrome; use Illuminate\Foundation\Testing\DatabaseMigrations; class ExampleTest extends DuskTestCase{ use DatabaseMigrations; /** * 一个基本的浏览器测试例子。 * * @return void */ public function testBasicExample() { $user = factory(User::class)->create([ 'email' => 'taylor@laravel.com', ]); $this->browse(function ($browser) use ($user) { $browser->visit('/login') ->type('email', $user->email) ->type('password', 'secret') ->press('Login') ->assertPathIs('/home'); }); } }在上面的範例中,
browse 方法接收了一個回呼參數。 Dusk 會自動將這個瀏覽器實例注入到回呼過程中,而這個瀏覽器實例可以和你的應用程式互動和斷言。
{tip} 這個測試範例可以用來測試建立多個瀏覽器有時候你可能需要多個瀏覽器才能正確的進行測試。例如,使用多個瀏覽器測試透過 websockets 進行通訊的線上聊天頁面。想要建立多個瀏覽器,需要在make:auth
指令產生的登入介面。
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'); });
改變瀏覽器視窗大小
你可以使用resize
方法去調整瀏覽器的視窗大小:
$browser->resize(1920, 1080);
maximize
方法可以將瀏覽器視窗最大化:
$browser->maximize();
#瀏覽器巨集
如果你想定義一個可以在各種測試中重複使用的自訂瀏覽器方法,可以在Browser
類別中使用macro
方法。通常,你應該從服務提供者的boot
方法中呼叫它:
<?php namespace App\Providers; use Laravel\Dusk\Browser; use Illuminate\Support\ServiceProvider; class DuskServiceProvider extends ServiceProvider{ /** * 注册Dusk的浏览器宏 * * @return void */ public function boot() { Browser::macro('scrollToElement', function ($element = null) { $this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);"); return $this; }); } }
macro
方法接收一個名稱作為第一個參數,第二個參數則是一個閉包。當呼叫瀏覽器巨集作為一個Browser
的實作的方法時,瀏覽器巨集的閉包將會執行:
$this->browse(function ($browser) use ($user) { $browser->visit('/pay') ->scrollToElement('#credit-card-details') ->assertSee('Enter Credit Card Details'); });
loginAs 方法來避免每個測驗都去登陸頁面登陸一次。
loginAs 可以使用使用者ID 或使用者模型實例:
$this->browse(function ($first, $second) { $first->loginAs(User::find(1)) ->visit('/home'); });
{note} 使用資料庫遷移就像上面的認證範例一樣,當你的測試案例需要遷移的時候,你不應該使用loginAs
方法後,該使用者的session 將會持久化的供其他測試用例使用。
RefreshDatabase trait。
RefreshDatabase trait 使用了不適用於 HTTP 請求的資料庫事務。取而代之,我們要用
DatabaseMigrations trait:
<?php namespace Tests\Browser; use App\User; use Tests\DuskTestCase; use Laravel\Dusk\Chrome; use Illuminate\Foundation\Testing\DatabaseMigrations; class ExampleTest extends DuskTestCase{ use DatabaseMigrations; }與元素互動
############################################################# ################Dusk 選擇器######選擇一個好的CSS 選擇器用於元素互動是編寫Dush 測試最困難的部分之一。隨著時間推移,前端的變更可能會導致類似以下的 CSS 選擇器中斷測試:###
// HTML... <button>Login</button> // Test... $browser->click('.login-page .container div > button');###Dusk 選擇器讓你專注於編寫有效的測試,而不是去記憶 CSS 選擇器。要定義一個選擇器,只需在你的 HTML 元素中新增一個 ###dusk### 屬性。然後,在選擇器前面加上###@### 去操作Dusk 測試中的附加元素:###
// HTML... <button dusk="login-button">Login</button> // Test... $browser->click('@login-button');####################點擊鏈接######要點選連結的話,你可以在瀏覽器實例上使用###clickLink### 方法。 ###clickLink### 方法將會點選指定顯示文字的連結:###
$browser->clickLink($linkText);#######{注意} 這個方法可以與 jQuery 互動。如果頁面上沒有 jQuery,Dusk 會自動將其註入頁面,保證在測試的期間可用。 #########################
文本、值 & 屬性
檢索和設定值
#Dusk 提供了幾種與目前顯示文本,值和屬性互動的方法。例如,要取得與指定選擇器相符的元素的“值”,請使用value
方法:
// 检索值... $value = $browser->value('selector'); // 设置值... $browser->value('selector', 'value');
檢索文字
# #text 這個方法可以用來匹配指定選擇器中元素的顯示文字:
$text = $browser->text('selector');#檢索屬性最後,
attribute這個方法可以用來匹配指定選擇器中元素的屬性:
$attribute = $browser->attribute('selector', 'value');#表單的使用輸入值Dusk 提供了與表單和input 元素互動的各種方法。首先讓我們來看一個在 input 方塊中輸入文字的範例:
$browser->type('email', 'taylor@laravel.com');注意, 雖然
type 方法可以傳遞 CSS 選擇器做為一個參數,但這並不是強制要求的。如果沒有提供 CSS 選擇器, Dusk 會搜尋與
name 屬性相同的 input 。如果還是沒有找到,Dusk 會嘗試尋找傳入值與
name 屬性相同的
textarea 。
append 方法:
$browser->type('tags', 'foo') ->append('tags', ', bar, baz');你可以使用
clear方法清除輸入值:
$browser->clear('email');下拉選單需要在下拉式選單中選擇值,你可以使用
select 方法。類似
type 方法,
select 方法並不是一定要傳入 CSS 選擇器。當使用
select 方法時,你應該傳遞選項實際的值而不是它的顯示文字:
$browser->select('size', 'Large');你也可以透過省略第二個參數來隨機選擇一個選項:
$browser->select('size');複選框使用“check” 複選框時,你可以使用
check 方法。像其他許多與 input 相關的方法,並不是必須傳入 CSS 選擇器。如果準確的選擇器無法找到的時候,Dusk 會搜尋能夠與
name 屬性相符的複選框:
$browser->check('terms');$browser->uncheck('terms');
使用「select」中單選按鈕選項時,你可以使用
radio 這個方法。像許多其他的與輸入相關的方法一樣, 它也不是必須傳入 CSS 選擇器。如果準確的選擇器無法被找到的時候, Dusk 會搜尋能夠與name
屬性或value
屬性相符的單選按鈕:$browser->radio('version', 'php7');
附件
attach
方法可以附加一個檔案到 file
input 元素中。像許多其他的與輸入相關的方法一樣,他也不是必須傳入 CSS 選擇器。如果準確的選擇器沒有被找到的時候, Dusk 會搜尋與name
屬性相符的檔案輸入框:
$browser->attach('photo', __DIR__.'/photos/me.png');
{注意} attach 方法需要使用PHP
Zip
擴展,你的伺服器必須安裝了此擴充。
使用鍵盤
keys
方法讓你可以再指定元素中輸入比type
方法更複雜的輸入序列。例如,你可以在輸入值的同時按下按鍵。在這個例子中,輸入 taylor
時, shift
鍵也同時被按下。當taylor
輸入完之後, 將會輸入otwell
而不會按下任何按鍵:
$browser->keys('selector', ['{shift}', 'taylor'], 'otwell');
你甚至可以在你的應用程式中選取某個元素之後按下「快速鍵」:
$browser->keys('.app', ['{command}', 'j']);
{提示} 所有套件在
{}
中的鍵盤按鍵,都對應定義於Facebook\WebDriver\WebDriverKeys
類別中,你可以在GitHub 中找到。
使用滑鼠
點擊元素
#click
方法可用於「點擊」與給定選擇器相符的元素:
$browser->click('.selector');
滑鼠懸停
mouseover
#方法可用於與給定選擇器匹配的元素的滑鼠懸停動作:
$browser->mouseover('.selector');
#拖曳
drag
方法用於將與指定選擇器匹配的元素拖曳到其它元素:
$browser->drag('.from-selector', '.to-selector');
或者,可以在單一方向上拖曳元素:
$browser->dragLeft('.selector', 10); $browser->dragRight('.selector', 10); $browser->dragUp('.selector', 10); $browser->dragDown('.selector', 10);##JavaScript 對話方塊Dusk 提供了幾種與JavaScript 對話方塊互動的方法:
// 等待对话框显示: $browser->waitForDialog($seconds = null); // 断言对话框已经显示,并且其消息与给定值匹配: $browser->assertDialogOpened('value'); // 在打开的 JavaScript 提示对话框中输入给定值: $browser->typeInDialog('Hello World');透過點擊確定按鈕關閉開啟的JavaScript 對話方塊:
$browser->acceptDialog();透過點擊取消按鈕關閉開啟的JavaScript 對話方塊(僅對確認對話方塊有效):
$browser->dismissDialog();選擇器作用範圍有時可能希望在給定的選擇器範圍內執行多個操作。例如,可能想要斷言表格中存在某些文本,然後點擊表格中的一個按鈕。可以使用
with 方法實作此需求。回呼函數內所有被執行的操作都被限定在原始的選擇器上:
$browser->with('.table', function ($table) { $table->assertSee('Hello World') ->clickLink('Delete'); });
等待元素
在測試大面積使用 JavaScript 的應用程式時,在進行測試之前,經常需要「等待」指定元素或資料可用。 Dusk 使之更容易。使用一系列方法,可以等到頁面元素可用,甚至給定的 JavaScript 運算式執行結果為 true
。
等待
如果需要測試暫停指定的毫秒數,可以使用pause
方法:
$browser->pause(1000);
等待選擇器
waitFor
方法可以用於暫停執行測試,直到頁面上與給定CSS 選擇器匹配的元素被顯示。預設情況下,將在暫停超過 5 秒後拋出異常。如果有必要,可以傳遞自訂逾時時長作為其第二個參數:
// 等待选择器 5 秒时间... $browser->waitFor('.selector'); // 等待选择器 1 秒时间... $browser->waitFor('.selector', 1);
也可以等待指定選擇器從頁面消失:
$browser->waitUntilMissing('.selector'); $browser->waitUntilMissing('.selector', 1);
選擇器可用時限定作用域範圍
偶爾可能希望等待選擇器然後與其互動。例如,可能希望等待模態視窗可用,然後點擊模態視窗的「確定」按鈕。 whenAvailable
方法能夠用於這種情況。給定回呼內的所有要執行的元素操作都會被限定在起始選擇器上:
$browser->whenAvailable('.modal', function ($modal) { $modal->assertSee('Hello World') ->press('OK'); });
等待文字
##waitForText#方法可以用於等待頁面上給定文字被顯示:
// 等待指定文本 5 秒时间... $browser->waitForText('Hello World'); // 等待指定文本 1 秒时间... $browser->waitForText('Hello World', 1);等待連結
waitForLink 方法用於等待給定連結文字在頁面顯示:
// 等待指定链接 5 秒时间... $browser->waitForLink('Create'); // 等待给定链接 2 秒时间... $browser->waitForLink('Create', 1);等待頁面跳轉在給予類似
$browser->assertPathIs('/home') 路徑斷言時,如果
window.location.pathname 被非同步更新,則斷言就會失敗。可以使用
waitForLocation 方法等待頁面跳到給定路徑:
$browser->waitForLocation('/secret');也可以等待被命名的路由跳轉:
$browser->waitForRoute($routeName, $parameters);#等待頁面重新載入如果要在頁面重新載入後斷言,可以使用
waitForReload# 方法:
$browser->click('.some-action') ->waitForReload() ->assertSee('something');等待JavaScript 表達式有時會希望暫停執行測試,直到給定的JavaScript 運算式執行結果為
true。可以使用
waitUntil 方法輕易地達成此目的。傳送一個表達式給此方法,不需要包含
return 關鍵字或結束分號:
//等待表达式为 true 5 秒时间... $browser->waitUntil('App.dataLoaded'); $browser->waitUntil('App.data.servers.length > 0'); // 等待表达式为 true 1 秒时间... $browser->waitUntil('App.data.servers.length > 0', 1);等待Vue 表達式下面的方法可以用於等待給定的Vue 元件屬性包含或不包含給定值:
// 等待组件属性包含给定值... $browser->waitUntilVue('user.name', 'Taylor', '@user'); // 等待组件属性不包含给定值... $browser->waitUntilVueIsNot('user.name', null, '@user');
等待回呼
Dusk 的許多「等待」方法都依賴底層的 waitUsing
方法。可以直接使用此方法來等待給定的回調回傳 true
。 waitUsing
方法接受等待的最大秒數,閉包執行的間隔時長,被執行的閉包,還有可靠的失敗訊息:
$browser->waitUsing(10, 1, function () use ($something) { return $something->isReady(); },"Something wasn't ready in time.");
做出Vue 斷言
Dusk 也允許你對Vue 元件資料的狀態作出斷言。例如,假設您的應用程式包含以下Vue 元件:
// HTML... <profile dusk="profile-component"></profile> // 定义组件... Vue.component('profile', { template: '<div>{{ user.name }}</div>', data: function () { return { user: { name: 'Taylor' } }; } });
你可以在Vue 元件的狀態上作出以下斷言:
/** * 一个简单的 Vue 测试例子。 * * @return void */ public function testVue(){ $this->browse(function (Browser $browser) { $browser->visit('/') ->assertVue('user.name', 'Taylor', '@profile-component'); }); }
可用的斷言
Dusk 提供了一系列可用的斷言方法。所有斷言如下:
##assertTitle##assertTitleContains
##assertUrlIs
assertSchemeIs
#assertSchemeIsNot
##assertHostIs##assertHostIsNot
assertPortIs
assertPortIsNot
assertPathBeginsWith
##assertPathIs
##assertPathIsNot
assertRouteIs
assertQueryStringHas
#assertQueryStringMissing
#assertQueryStringMissing
assertQueryStringMissing
assertFragmentIs
##assertFragmentBeginsWith
#assertFragmentIsNot
assertHasCookie
asserCookieMissing ##assertCookieValue
##assertPlainCookieValue
##assertSee
#assertDontSee
#assertSeeIn ##assertDontSeeIn
##assertSourceHas
##assertSourceMissing
assertSeeLink
#assertDontSeeLink
assertInputValue##assertInputValueIsNot
##assertChecked
#assertNotChecked
assertRadioSelected
##asserRadioNotSelected##assertRadioSelected
##assertSelected
#assertNotSelected
assertSelectHasOptions
assertSelectMissingOptions
##assertSelectHasOption
#assertValue
##assertVisible
#assertPresent
#assertMissing ##assertDialogOpened
##assertEnabled
#assertDisabled
#assertFocused
assertNotFocused
##assertVue
#assertVueIsNot##assertVueContains
assertVueDoesNotContain
#assertTitle
斷言網頁標題符合指定的文字:
$browser->assertTitle($title);
assertTitleContains
斷言網頁標題包含指定的文字:
$browser->assertTitleContains($title);
#assertUrlIs
斷言目前URL (不含查詢字串) 符合指定的字串:
$browser->assertUrlIs($url);
#assertSchemeIs
斷言當前URL 匹配與給定的字串匹配:
$browser->assertSchemeIs($scheme);
assertSchemeIsNot斷言目前URL 符合與給定的字串不符:
$browser->assertSchemeIsNot($scheme);
assertHostIs斷言目前URL 的host 與給定的值符合:
$browser->assertHostIs($host);##assertHostIsNot
$browser->assertHostIsNot($host);#assertPortIs
##斷言目前URL的連接埠值與給定的值相符:$browser->assertPortIs($port);
#assertPortIsNot
斷言目前URL 的埠值與給定的值不符合:$browser->assertPortIsNot($port);
assertPathBeginsWith
斷言目前URL 開始於指定的路徑: $browser->assertPathBeginsWith($path);
assertPathIs
斷言目前路徑符合指定的路徑:$browser->assertPathIs('/home');
#assertPathIsNot
斷言目前路徑不符合指定的路徑:
$browser->assertPathIsNot('/home');
assertRouteIs
斷言目前URL 符合指定的命名路由的URL:
$browser->assertRouteIs($name, $parameters);###assertQueryStringHas### ###斷言存在指定的查詢字串參數:###
$browser->assertQueryStringHas($name);###斷言指定的查詢字串參數存在,且該參數的值為指定的值:###
$browser->assertQueryStringHas($name, $value);######## #############assertQueryStringMissing######斷言不存在指定的查詢字串參數:###
$browser->assertQueryStringMissing($name);################# ##
assertFragmentIs
斷言目前的分片符合指定的分片:
$browser->assertFragmentIs('anchor');
assertFragmentBeginsWith
斷言目前的分片以指定的分片開頭:
$browser->assertFragmentBeginsWith('anchor');
#assertFragmentIsNot
斷言目前的分片不符合指定的分片:
$browser->assertFragmentIsNot('anchor');
#assertHasCookie
斷言存在指定的cookie:
$browser->assertHasCookie($name);
assertCookieMissing
斷言不存在指定的cookie:
$browser->assertCookieMissing($name);
#assertCookieValue
斷言cookie 存在指定的值:
$browser->assertCookieValue($name, $value);assertPlainCookieValue斷言未加密的cookie 存在指定的值:
$browser->assertPlainCookieValue($name, $value);
$browser->assertSee($text);##斷言目前頁不存在指定的文字:
$browser->assertDontSee($text);
$browser->assertSeeIn($selector, $text);
$browser->assertDontSeeIn($selector, $text);
$browser->assertSourceHas($code);
$browser->assertSourceMissing($code);
$browser->assertSeeLink($linkText);
$browser->assertDontSeeLink($linkText);
$browser->assertInputValue($field, $value);
$browser->assertInputValueIsNot($field, $value);############################################################################################ #assertChecked######斷言指定的複選框被選取:###
$browser->assertChecked($field);##################
assertNotChecked
斷言指定的核取方塊未選取:
$browser->assertNotChecked($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);#assertValue斷言選擇器範圍內的元素存在指定的值:
$browser->assertValue($selector, $value);#assertVisible#斷言選擇器範圍內的元素可見:
$browser->assertVisible($selector);
斷言選擇器範圍內的元素是存在的:
$browser->assertPresent($selector);
斷言選擇器範圍內的元素不存在:
$browser->assertMissing($selector);assertDialogOpened斷言含有指定訊息的JavaScript 對話方塊已經開啟:
$browser->assertDialogOpened($message);
# assertEnabled
斷言指定的欄位是啟用的:$browser->assertEnabled($field);
$browser->assertDisabled($field);#####################assertFocused######斷言焦點在於指定的欄位:###
$browser->assertFocused($field);# ####################assertNotFocused#######斷言焦點不在指定的欄位:###
$browser->assertNotFocused($field);############# #########assertVue######斷言Vue 元件資料的屬性符合指定的值:###
$browser->assertVue($property, $value, $componentSelector = null);##################
assertVueIsNot
斷言Vue 元件資料的屬性不符合指定的值:
$browser->assertVueIsNot($property, $value, $componentSelector = null);##assertVueContains斷言Vue 元件資料的屬性是數組,並且數組包含指定的值:
$browser->assertVueContains($property, $value, $componentSelector = null);
斷言Vue 元件資料的屬性是數組,且數組不包含指定的值:
$browser->assertVueDoesNotContain($property, $value, $componentSelector = null);
##有時候,需要測試一系列複雜的動作,這會使得測試程式碼難以閱讀和理解。透過頁面可以定義出語義化的動作,然後在指定頁面中可以使用單一方法。頁面還可以定義應用程式或單一頁面通用選擇器的捷徑。
Artisan 指令可以產生頁面物件。所有的頁面物件都位於tests/Browser/Pages
目錄:php artisan dusk:page Login
url
,assert 和
elements。這裡我們先詳述
url 和
assert 方法,
elements 方法將會
選擇器簡寫 中詳述。
url
方法應該會傳回表示頁面 URL 的路徑。 Dusk 將會在瀏覽器中使用這個URL 來導航到特定頁面:/** * 获得页面 URL 路径。 * * @return string */ public function url(){ return '/login'; }
##assert
方法可以作出任何斷言來驗證瀏覽器是否在指定頁面上。這個方法並不是必須的。你可以根據你自己的需求來做出這些斷言。這些斷言會在你導航到這個頁面的時候自動執行:
/** * 断言浏览器当前处于指定页面。 * * @return void */ public function assert(Browser $browser){ $browser->assertPathIs($this->url()); }
方法導航至頁面:
use Tests\Browser\Pages\Login;$browser->visit(new Login);
有時候,你可能已經在指定頁面了,你需要的只是「載入」目前頁面的選擇器和方法到當前測試中來。常見的例子有:當你按下一個按鈕的時候,你會被重新導向到指定頁面,而不是直接導覽至指定頁面。在這種情況下,你需要使用 on
方法來載入頁面:
use Tests\Browser\Pages\CreatePlaylist;$browser->visit('/dashboard') ->clickLink('Create Playlist') ->on(new CreatePlaylist) ->assertSee('@create');
現在你可以用這個簡寫來取代先前在頁面中使用的完整CSS 選擇器: 安裝Dusk 之後, 除了頁面中已經定義的預設方法之外,你還可以定義在整個測試過程中會使用到的其他方法。例如,假設我們正在開發一個音樂管理應用,在應用中都可能需要一個公共的方法來創建列表,而不是在每一頁、每一個測試類中都重寫一遍創建播放列表的邏輯,這時候你可以在你的頁面類別中定義一個 方法被定義之後,你可以在任何使用到該頁的測試中使用這個方法了。瀏覽器實例會自動傳遞該頁面方法: 元件類似Dusk 的「頁面對象」,不過它更多的是貫穿整個應用程式中頻繁重用的UI 和功能片段,比如說導航條或訊息通知彈窗。因此,元件並不會綁定於某個明確的 URL。 #為了產生一個元件,使用Artisan 指令 如上所示,這是產生一個「日期選擇器」(date picker) 元件的範例,這個元件可能會貫穿使用在你應用程式的許多頁面中。在整個測試套件的大量測試頁面中,手動編寫日期選擇的瀏覽器自動化邏輯會非常麻煩。更方便的替代方法是,定義一個表示日期選擇器的Dusk 元件,然後把自動化邏輯封裝在該元件內: 元件定義一旦完成,在任何測試頁面的日期選擇器中選定日期就很輕鬆了。並且,如果需要修改選定日期的邏輯,僅修改該元件即可: 如果使用 CircleCI 執行 Dusk 測試,則可以參考此設定檔作為起點。與TravisCI 一樣,我們將使用 在Codeship 中執行Dusk 測試,需要在你的Codeship 專案中加入以下指令。當然,這些命令只是作為範例,你可以根據需要隨意添加額外的命令: Travis CI選擇器簡寫
elements
方法允許你為頁面中的任何 CSS 選擇器定義簡單易記的簡寫。例如,讓我們為應用程式登入頁面中的email
輸入框定義一個簡寫:/**
* 获取页面的元素简写。
*
* @return array
*/
public function elements(){
return [
'@email' => 'input[name=email]',
];
}
$browser->type('@email', 'taylor@laravel.com');
全域的選擇器簡寫
Page
基底類別存放在你的tests/Browser/Pages
目錄。類別中包含一個siteElements
方法,這個方法可以用來定義全域的選擇器簡寫,這樣在你應用程式中每個頁面都可以使用這些全域選擇器簡寫了:/**
* 获取站点全局的选择器简写。
*
* @return array
*/
public static function siteElements(){
return [
'@element' => '#selector',
];
}
頁面方法
createPlaylist
方法:<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
class Dashboard extends Page{
// 其他页面方法...
/**
* 创建一个新的播放列表。
*
* @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');
}
}
use Tests\Browser\Pages\Dashboard;$browser->visit(new Dashboard)
->createPlaylist('My Playlist')
->assertSee('My Playlist');
#元件
#元件的產生
dusk:component
即可生成組件。新產生的元件位於test/Browser/Components
目錄中:php artisan dusk:component DatePicker
<?php
namespace Tests\Browser\Components;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Component as BaseComponent;
class DatePicker extends BaseComponent{
/**
* 获取组件的 root selector
*
* @return string
*/
public function selector()
{
return '.date-picker';
}
/**
* 浏览器包含组件的断言
*
* @param Browser $browser
* @return void
*/
public function assert(Browser $browser)
{
$browser->assertVisible($this->selector());
}
/**
* 读取组件的元素快捷方式
*
* @return array
*/
public function elements()
{
return [
'@date-field' => 'input.datepicker-input',
'@month-list' => 'div > div.datepicker-months',
'@day-list' => 'div > div.datepicker-days',
];
}
/**
* 选择给定日期
*
* @param \Laravel\Dusk\Browser $browser
* @param int $month
* @param int $day
* @return void
*/
public function selectDate($browser, $month, $day)
{
$browser->click('@date-field')
->within('@month-list', function ($browser) use ($month) {
$browser->click($month);
})
->within('@day-list', function ($browser) use ($day) {
$browser->click($day);
});
}
}
元件的使用
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Tests\Browser\Components\DatePicker;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ExampleTest extends DuskTestCase{
/**
* 基本的组件测试示例
*
* @return void
*/
public function testBasicExample()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->within(new DatePicker, function ($browser) {
$browser->selectDate(1, 2018);
})
->assertSee('January');
});
}
}
持續整合
#CircleCI
php artisan serve
指令啟動PHP 的內建Web 伺服器:version: 2jobs:
build:
steps:
- run: sudo apt-get install -y libsqlite3-dev
- run: cp .env.testing .env
- run: composer install -n --ignore-platform-reqs
- 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
Codeship
phpenv local 7.2
cp .env.testing .env
mkdir -p ./bootstrap/cache
composer install --no-interaction --prefer-dist
php artisan key:generate
nohup bash -c "php artisan serve 2>&1 &" && sleep 5
php artisan dusk
Heroku CI
中執行Dusk 測試時,請將下列Google Chrome 建置套件和腳本新增至你的Heroku
app.json在
Travis CI
中執行Dusk 測試時,你可以參考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
before_script:
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
- php artisan serve &
script:
- php artisan dusk
APP_URL=http://127.0.0.1:8000###本篇首發在###LearnKu.com### 網站。 ######