天天看點

UI自動化之PO模型

PO模型

    • 簡述
    • 測試架構發展
    • 設計分層
    • 設計理念
    • po設計實作
    • 示例
    • 總結思考

簡述

PageObject(頁面即對象),PO模式是UI自動化測試實作中廣泛使用的模式,它基本上由應用程式頁面元素和類之間的映射組成;它還使用其元素在該頁面上定義了使用者操作;po模式和工具(selenium)結合使用時,可以在代碼維護方面提供具有高業務價值和低成本的自動化測試。

重構,維護成本低

測試代碼是代碼,是以重構測試自動化代碼與重構應用程式代碼一樣重要。否則,測試将很難維護,最好扔掉所有東西,然後重新開始。通過遵循一些技巧可以避免這種成本,這些技巧可幫助減少代碼量,功能檔案和步驟定義的維護成本。

在Web應用程式的UI中,您的測試與某些區域互動。Page Object隻是将它們模組化為測試代碼中的對象。這減少了重複代碼的數量,并且意味着如果UI更改,則僅需要在一個地方應用此修複程式。

測試架構發展

1,線形腳本

少量代碼實作的針對某一個功能的自動化操作步驟;

特點:上手簡單,少量代碼,無擴充性,無複用性,基本不具備業務價值

2,資料驅動+關鍵字驅動(selenium二次封裝)

測試腳本與測試資料分離,通過給定的資料執行對應的測試步驟,通過編寫用例資料與維護用例資料來實作自動化測試,當頁面ui發生變動後隻需要修改對應的資料檔案,而不需要修改測試腳本代碼;

特點:架構開發有一定難度,用例編寫變得簡單,易于維護,具有很高的業務價值,擴充性底;

3,po模型+資料驅動+關鍵字驅動

在資料驅動+關鍵字驅動基礎上更新,保留其本身的優點,并有效的提高了擴充性,靈活性,要求有一定的代碼功底;

設計分層

UI自動化之PO模型

設計架構圖

UI自動化之PO模型

設計理念

1,可以将PageObject視為同時面向兩個方向;面向測試的開發人員,它們代表特定頁面提供的服務。面對開發人員,它們應該是唯一了解頁面(或頁面一部分)HTML結構的唯一事物。

2,将頁面對象上的方法視為提供“服務”是頁面最基本的資訊。鼓勵測試開發人員在PageObject開發中隻考慮頁面互動的服務,而不是基礎的WebDriver的實作。

3, PageObject上的方法應傳回其他PageObjects。這意味着我們可以通過我們的應用程式有效地模拟使用者的操作流程。也意味着,頁面之間的互動流程發生改變(例如,登入頁面要求使用者在首次登入服務時更改密碼,而之前沒有這樣做),隻需更改适當的方法即可。

這種方法的一個後果是,可能有必要對成功和不成功的登入進行模組化,或者根據應用程式的狀态,單擊可能會産生不同的結果。發生這種情況時,通常在PageObject上具有多個方法:

public class LoginPage {
    public HomePage loginAs(String username, String password) {
        // ... 登入成功
        return new HomePage(driver);
    }
    
    public LoginPage loginAsExpectingError(String username, String password) {
        //  ... 登入失敗,可能是使用者名密碼錯誤
        return new LoginPage(driver);
    }
    
    public String getErrorMessage() {
        // 擷取登入失敗的頁面提示内容并傳回
        return message
    }
}
           

4,PageObjects不應該負責對頁面狀态進行斷言。

例如:

public void testLoginAsExpectingError() {
    LoginPage loginPage = new LoginPage(driver);
    loginPage.loginAsExpectingError(username, password)
    loginPage.assertLoginAsExpectingErrorMessage("使用者名錯誤");
}
           

優化寫法:

public void testLoginAsExpectingError() {
    LoginPage loginPage = new LoginPage(driver);
    loginPage.loginAsExpectingError(username, password)
    assertFalse(loginPage.getErrorMessage("使用者名錯誤");
}
           

po設計實作

1,頁面對象化

一個頁面包含多個對象,每個對象都會提供對應的頁面服務;

2,目标元素屬性化

每一個提供服務的目标元素都會映射為PageObjects中的屬性

3,操作函數化

對目标元素的操作,以及頁面所能提供的服務進行函數化

4,WebDriver底層實作封裝

封裝基礎的WebDriver元素操作方法,為每一個PageObjects提供直接調用的能力,而無需考慮怎麼實作;

示例

public class LoginPage {
    private final WebDriver driver;

    public LoginPage(WebDriver driver) {
        this.driver = driver;

        // 檢查我們在正确的頁面上。
        if (!"Login".equals(driver.getTitle())) {
            // 或者,我們可以導航到登入頁面,也許先登出
            throw new IllegalStateException("這不是登入頁面");
        }
    }

    //登入頁面包含幾個将表示為WebElements的HTML元素。
    //這些元素的定位符僅應定義一次。
    By usernameLocator = By.id("username");
    By passwordLocator = By.id("passwd");
    By loginButtonLocator = By.id("login");

    //登入頁面允許使用者在使用者名字段中輸入其使用者名
    public LoginPage typeUsername(String username) {
        //這是唯一“知道”如何輸入使用者名的地方
        driver.findElement(usernameLocator).sendKeys(username);

        //傳回目前頁面對象,因為此操作不會導航到由另一個PageObject表示的頁面
        return this;
    }

    //登入頁面允許使用者在密碼字段中輸入密碼
    public LoginPage typePassword(String password) {
        //這是唯一“知道”如何輸入密碼的地方
        driver.findElement(passwordLocator).sendKeys(password);
        return this;
    }

    //登入頁面允許使用者送出登入表單
    public HomePage submitLogin() {
        //這是送出登入表單并希望目的地為首頁的唯一位置。
        driver.findElement(loginButtonLocator).submit();

        //傳回代表目标的新頁面對象。
        //移至其他地方(例如,法律免責聲明),然後更改方法簽名
        //對于此方法,将意味着不會編譯所有依賴此行為的測試。
        return new HomePage(driver);
    }

    //登入頁面允許使用者在知道輸入了無效的使用者名和/或密碼的情況下送出登入表單
    public LoginPage submitLoginExpectingFailure() {
        //這是唯一送出登入表單并由于登入失敗而期望目的地為登入頁面的位置。
        driver.findElement(loginButtonLocator).submit();

        //傳回代表目标的新頁面對象。 送出帶有憑據的登入名後,是否應該将使用者導航到首頁?
        //預期登入失敗,該腳本在嘗試執行個體化LoginPage PageObject時将失敗。
        return new LoginPage(driver);
    }

    //從概念上講,登入頁面為使用者提供了能夠“登入”的服務
    //使用使用者名和密碼的應用程式。
    public HomePage loginAs(String username, String password) {
        //輸入使用者名,密碼和送出登入名的PageObject方法已經定義,在此不再重複。
        typeUsername(username);
        typePassword(password);
        return submitLogin();
    }
}
           

總結思考

思考

1,關于頁面對象是應該自己包含斷言,還是隻是為測試腳本提供資料來斷言,存在意見分歧。

主張在頁面對象中包含斷言的人說,這有助于避免斷言在測試腳本中的重複。

無斷言頁面對象的擁護者說,包含斷言将提供對頁面資料的通路與斷言邏輯的職責混合在一起,并導緻膨脹的頁面對象。

比較好的處理方案,頁面對象中沒有斷言。可以通過為常見的斷言提供斷言庫來避免重複-這也可以使提供良好的斷言更加容易

2,這裡有一個論點,名稱“頁面對象”具有誤導性,因為它使您認為每頁應該隻有一個頁面對象。

PageObject不必代表整個頁面。它可能代表一個在網站或頁面中多次出現的部分,例如網站導航。基本原則是,測試套件中隻有一個地方可以了解特定頁面(一部分)的HTML結構。

3, 常見的建議是讓頁面對象負責建立其他頁面對象,以響應諸如導航之類的事情。但是,一些從業者更喜歡頁面對象傳回一些通用的浏覽器上下文,并且測試根據測試流程(尤其是條件流程)控制在該上下文之上建構哪些頁面對象。他們的偏好是基于這樣一個事實,即測試腳本知道接下來要通路哪些頁面,并且不需要在頁面對象本身中重複這些知識。

總結

1,公共方法代表頁面提供的服務

2,盡量不要暴露頁面的内部

3,通常不做斷言

4,方法傳回其他PageObjects

5,無需代表整個頁面

6,相同動作的不同結果被模組化為不同的方法

參考文獻

selenium官網文檔:

  • PageObjects
  • 基類支援
  • Bot Style Tests

其他:

  • 馬丁福勒:PageObject核心理念
  • Cucumber+selenium+po模式實作