瀏覽器測試 Dusk


#####Travis CI################# ##########

Browser Tests (Laravel Dusk)

#介紹

Laravel Dusk 提供了富有表現力、簡單易用的瀏覽器自動化及測試API 。預設情況下,Dusk 不需要在你的機器上安裝 JDK 或 Selenium 。而是需要使用單獨的
ChromeDriver
進行安裝。當然,你也可以自由使用其他的與 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 檔。

執行測試的時候,Dusk 會備份你的

.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} 這個測試範例可以用來測試

make:auth 指令產生的登入介面。

建立多個瀏覽器

有時候你可能需要多個瀏覽器才能正確的進行測試。例如,使用多個瀏覽器測試透過 websockets 進行通訊的線上聊天頁面。想要建立多個瀏覽器,需要在

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');
   });

##認證

你可能常常會測試一些需要認證的頁面。你可以使用 Dusk 的

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 方法。可以直接使用此方法來等待給定的回調回傳 truewaitUsing 方法接受等待的最大秒數,閉包執行的間隔時長,被執行的閉包,還有可靠的失敗訊息:

$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

斷言目前URL 的host 與給定的值不符:

$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);

##assertSee

斷言目前頁存在指定的文字:
$browser->assertSee($text);

#assertDontSee

##斷言目前頁不存在指定的文字:
$browser->assertDontSee($text);

assertSeeIn

斷言選擇器範圍內存在指定的文字:
$browser->assertSeeIn($selector, $text);

#assertDontSeeIn

斷言選擇器範圍內不存在指定的文字:
$browser->assertDontSeeIn($selector, $text);

#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);

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);


assertPresent

斷言選擇器範圍內的元素是存在的:

$browser->assertPresent($selector);

assertMissing

斷言選擇器範圍內的元素不存在:

$browser->assertMissing($selector);

assertDialogOpened

斷言含有指定訊息的JavaScript 對話方塊已經開啟:

$browser->assertDialogOpened($message);

# assertEnabled

斷言指定的欄位是啟用的:

$browser->assertEnabled($field);

#########assertDisabled########斷言指定的欄位是停用的:###
$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);

##assertVueDoesNotContain

斷言Vue 元件資料的屬性是數組,且數組不包含指定的值:

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

##有時候,需要測試一系列複雜的動作,這會使得測試程式碼難以閱讀和理解。透過頁面可以定義出語義化的動作,然後在指定頁面中可以使用單一方法。頁面還可以定義應用程式或單一頁面通用選擇器的捷徑。

#產生頁面

#dusk:page

Artisan 指令可以產生頁面物件。所有的頁面物件都位於tests/Browser/Pages 目錄:

php artisan dusk:page Login

##設定頁面
頁面預設有3 個方法:

url

assertelements。這裡我們先詳述 urlassert 方法, elements 方法將會 選擇器簡寫 中詳述。

url
方法

url

方法應該會傳回表示頁面 URL 的路徑。 Dusk 將會在瀏覽器中使用這個URL 來導航到特定頁面:

/**
 * 获得页面 URL 路径。
 *
 * @return string
 */
 public function url(){   
  return '/login';
 }

#assert
方法

##assert方法可以作出任何斷言來驗證瀏覽器是否在指定頁面上。這個方法並不是必須的。你可以根據你自己的需求來做出這些斷言。這些斷言會在你導航到這個頁面的時候自動執行:

/**
 * 断言浏览器当前处于指定页面。
 *
 * @return void
 */
 public function assert(Browser $browser){ 
    $browser->assertPathIs($this->url());
   }

#導航至頁面

#一旦頁面配置好之後,你可以使用
visit

方法導航至頁面:

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');

選擇器簡寫

elements 方法允許你為頁面中的任何 CSS 選擇器定義簡單易記的簡寫。例如,讓我們為應用程式登入頁面中的email 輸入框定義一個簡寫:

/**
 * 获取页面的元素简写。
 *
 * @return array
 */
 public function elements(){ 
    return [  
          '@email' => 'input[name=email]',   
         ];
       }

現在你可以用這個簡寫來取代先前在頁面中使用的完整CSS 選擇器:

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

全域的選擇器簡寫

安裝Dusk 之後,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 的「頁面對象」,不過它更多的是貫穿整個應用程式中頻繁重用的UI 和功能片段,比如說導航條或訊息通知彈窗。因此,元件並不會綁定於某個明確的 URL。

#元件的產生

#為了產生一個元件,使用Artisan 指令dusk:component即可生成組件。新產生的元件位於test/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{   
     /**
     * 获取组件的 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

如果使用 CircleCI 執行 Dusk 測試,則可以參考此設定檔作為起點。與TravisCI 一樣,我們將使用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

Codeship 中執行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

##Heroku CI

Heroku CI

中執行Dusk 測試時,請將下列Google Chrome 建置套件和腳本新增至你的Heroku

app.json

檔案:

{  "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" 
                    }  
                  }  
            }
       }

Travis CI在 Travis CI 中執行Dusk 測試時,你可以參考

.travis.yml
的配置。由於 Travis CI 不是圖形環境,因此我們需要採取一些額外的步驟來啟動 Chrome 瀏覽器。此外,我們將使用php artisan serve 啟動PHP 的內建Web 伺服器:
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
在###.env.testing### 檔案中,調整###APP_URL###的值:###
APP_URL=http://127.0.0.1:8000
###本篇首發在###LearnKu.com### 網站。 ######