前言
Page Object(頁面對象)模式,是Selenium實戰中最為流行,並且被自動化測試同學所熟悉和推崇的一種設計模式之一。在設計測試時,把頁面元素定位和元素操作方法按照頁面抽象出來,分離成一定的對象,然后再進行組織。
相信每個做自動化測試的同學,一定會遇到這樣一個非常頭疼的問題,那就是頁面變化,如果沒有使用Page Object設計模式,這就意味着以前的定位元素方法不能用了,需要重新修改元素定位方式。你需要一個一個從測試腳本中把需要修改的元素定位方式找出來,然后再進行修改。這勢必會使腳本維護的成本變高,顯然這樣的自動化腳本就不會有人願意使用。
那這時我們使用page object模式就可以解決這個問題了。
PageObject 的優點
- 減少代碼冗余
- 業務和實現分離
- 降低代碼維護成本
Page Object模式
Page Object 見名知意,就是頁面對象,並將頁面元素定位方法和元素操作進行分離。
在實際自動化測試實戰過程中,我們一般對腳本的實現分為三層:
- 對象層:用於存放頁面元素定位和控件操作。
- 邏輯層:則是一些封裝好的功能用例模塊。
- 業務層:則是我們真正的測試用例的操作部分。
使用 Page Object 類來分離頁面元素
我們以360影視登錄頁為測試對象,先創建一個包com.pageobject.demo
對象層
首先我們新建一個類LoginPage,登錄頁面內編寫需要操作的元素定位方式和控件操作,具體代碼示例如下:
package com.pageobject.demo; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.testng.Assert; /** * @author rongrong * 對象庫層代碼案例 */ public class LoginPage { WebDriver driver; //定位 用戶名輸入框 public static By userNameInput = By.name("loginname"); //定位 密碼輸入框 public static By passWordInput = By.name("loginpassword"); //定位 登錄按鈕 public static By loginBtn = By.linkText("立即登錄"); //定位 提示錯誤信息 public static By errorMsg = By.cssSelector("[class='b-signin-error js-b-signin-error error-2']"); public LoginPage(WebDriver driver) { this.driver = driver; } /** * 用戶名輸入操作 * * @param userName */ public void sendKeysUserName(String userName) { driver.findElement(userNameInput).clear(); driver.findElement(userNameInput).sendKeys(userName); } /** * 密碼輸入操作 * * @param passWord */ public void sendKeysPassWord(String passWord) { driver.findElement(passWordInput).clear(); driver.findElement(passWordInput).sendKeys(passWord); } }
這里我只對用戶名和密碼輸入框進行了封裝,有興趣的同學也可以接着進行全部元素操作封裝
操作層
我們再新建一個類LoginMovies,用於登錄邏輯的封裝,供業務層調用,具體代碼示例如下:
package com.pageobject.demo; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.testng.Assert; /** * @author rongrong * 操作層代碼案例 */ public class LoginMovies { WebDriver driver; public LoginMovies(WebDriver driver) { this.driver = driver; } /** * 登錄操作 * * @param userName * @param pwd * @param expected */ public void login(String userName, String pwd, String expected) { LoginPage loginPage = new LoginPage(driver); //輸入用戶名 loginPage.sendKeysUserName(userName); //輸入密碼 loginPage.sendKeysPassWord(pwd); //點擊登錄 driver.findElement(LoginPage.loginBtn).click(); //獲取提示語操作 String msg = driver.findElement(LoginPage.errorMsg).getText(); Assert.assertEquals(msg, expected); } }
業務層
最后我們新建一個類TestPageObject,用於業務層的封裝,具體代碼示例如下:
package com.pageobject.demo; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.util.concurrent.TimeUnit; /** * @author rongrong * 業務層代碼案例 */ public class TestPageObject { /** * 360影視登錄頁 */ public static final String URL = "https://i.360kan.com/login"; WebDriver driver; @BeforeClass public void BeforeClass() { //設置系統變量,並設置chromedriver的路徑為系統屬性值 System.setProperty("webdriver.chrome.driver", "tool/chromedriver.exe"); //實例ChromeDriver driver = new ChromeDriver(); driver.get(URL); driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); driver.manage().window().maximize(); } /** * 測試登錄 */ @Test public void testLogin() { //實例化操作對象 LoginMovies loginMovies = new LoginMovies(driver); loginMovies.login("your userName", "your passWord", "輸入手機號不合法"); } @AfterClass public void closedChrome() { driver.quit(); } }
小結
雖然該實現方法看上去復雜多了,但其中的設計好處是不同層關心不同的問題。頁面對象只關心元素的定位,測試用例只關心測試數據。
LoginPage類中主要對登錄頁面上元素進行封裝,使其成為具體的操作方法。如對用戶名、密碼框都封裝成方法,然后定義login(String userName, String pwd, String expected)方法將單個元素操作組成一個完整的動作,包含輸入用戶名、密碼並點擊登錄按鈕等。
使用時將driver、username、pwd、expected作為函數的入參,這樣的方法具有很強的可重用性。
最后使用testLogin()方法進行用戶操作行為,現在只關心用哪個瀏覽器、登錄的用戶名和密碼是什么,至少輸入框、按鈕是如何定位的,則不關心。即實現了不同層關心不同問題。如果再有定位元素變化,只需LoginPage這個類維護即可,顯然方便了很多。