內容轉載自 https://www.cnblogs.com/yytesting/p/6973474.html
頁面對象(Page Object)模式是目前自動化測試領域普遍使用的設計模式之一,此模式可以大大提高測試代碼的復用率,提高測試腳本的編寫效率和維護效率,是中級自動化測試工程師的必備技能之一。
使用面向對象的設計模式,頁面對象模型將測試代碼和被測試頁面的頁面元素及其操作方法進行分離,以此降低頁面元素變化對測試代碼的影響。每一個被測試頁面都會被單獨定義為一個類,類中會定位所有需進行測試操作的頁面元素對象,並且定義操作每一個頁面元素對象的方法。
例如,登錄頁面包括一個用戶輸入框和一個密碼輸入框,還有一個登錄按鈕。
我們聲明一個名為Login的類,並且通過定位表達式找到用戶名和密碼輸入框,並賦予類中的成員變量,分別定義輸入用戶名的方法、輸入密碼的方法和單擊登錄按鈕的方法。
測試代碼要完成登錄測試,只需要調用Login類中輸入用戶名的方法、輸入密碼的方法和單擊登錄按鈕的方法即可完成一個登錄操作。如果登錄頁面的用戶輸入框、密碼輸入框或者登錄按鈕發生了位置變化,我們只需要修改Login類中的相關定位表達式和操作方法就可以完成維護,測試邏輯的腳本甚至不需要改變。
如果用戶沒有使用此模式,那么將登錄過程都用相同的代碼進行實現。如果在測試過程中需要多次登錄操作,那么只能粘貼相同的代碼來簡化編寫工作。但是可怕的情況是一旦頁面元素發生了一點點改變,那么測試人員需要人工去把所有涉及變化的邏輯一一修改,會在不同的測試代碼中進行搜索和修改,這樣不但大大增加了工作量,而且很容易出現修改錯誤的情況。使用了頁面對象模式,只需要修改一下唯一的Login類,就完成了大部分的維護工作。
測試網址:https://www.126.com/
使用PageFactory類
1.使用PageFactory類給測試類提供待操作的頁面元素
先在src中新建一個package,名字為pageobjects,在下面新建一個頁面對象類LoginPage。新建一個package,名字為testScripts,在下面新建一個測試類Test126mail。
LoginPage類的源代碼如下:
public class LoginPage1 { //使用FindBy注解,定位到需要操作的頁面元素 @FindBy(name="email") public WebElement userName; @FindBy(name="password") public WebElement password; @FindBy(id="dologin") public WebElement loginButton; public LoginPage1(WebDriver driver) { PageFactory.initElements(driver, this); } }
Test126mail類的源代碼如下:
public class Test126mail1 { private WebDriver driver; private String baseUrl = "https://www.126.com/"; @Test public void testLogin() throws InterruptedException { //訪問被測試的網址 driver.get(baseUrl); Thread.sleep(5000); driver.switchTo().frame(0); //生成一個LoginPage的實例 LoginPage1 loginPage = new LoginPage1(driver); //直接使用頁面對象的用戶元素,輸入用戶名 loginPage.userName.sendKeys("郵箱名"); //直接使用頁面對象的用戶元素,輸入密碼 loginPage.password.sendKeys("郵箱密碼"); //直接使用頁面對象的登錄按鈕對象,進行單擊操作 loginPage.loginButton.click(); //等待5秒 Thread.sleep(5000); driver.switchTo().defaultContent(); //斷言登錄后的頁面是否包含"收件箱"關鍵字,來驗證是否登錄成功 Assert.assertTrue(driver.getPageSource().contains("收件箱")); Thread.sleep(3000); } @BeforeMethod public void beforeMethod() { System.setProperty("webdriver.chrome.driver", "C:\\Users\\lenovo\\AppData\\Local\\Google\\Chrome\\chromedriver.exe"); driver = new ChromeDriver(); } @AfterMethod public void afterMethod() { /*driver.quit();*/ } }
從上面的實例中我們可以看到,頁面元素的定位均在LoginPage類中實現了。如果頁面元素發生了一定程度的調整,測試人員只需要修改LoginPage類中的定位表達式就可以完成基本的維護工作,測試類代碼無需進行調整,從而降低了測試代碼的維護工作。
2.使用PageFactory類封裝頁面元素的操作方法
上面我們只為測試類提供了頁面元素來進行操作,並沒有在頁面對象類中實現頁面元素的操作方法。
LoginPage類的源代碼如下:
public class LoginPage2 { @FindBy(name="email") public WebElement userName; @FindBy(name="password") public WebElement password; @FindBy(id="dologin") public WebElement loginButton; public String url="https://www.126.com/"; public WebDriver driver; //構造函數,生成瀏覽器對象,初始化PageFactory對象 public LoginPage2() { System.setProperty("webdriver.chrome.driver", "C:\\Users\\lenovo\\AppData\\Local\\Google\\Chrome\\chromedriver.exe"); driver = new ChromeDriver(); PageFactory.initElements(driver, this); } //訪問被測試網址的封裝方法 public void load() { driver.get(url); } //關閉瀏覽器的封裝方法 public void quit() { driver.quit(); } //登錄操作的封裝方法 public void login() throws InterruptedException { Thread.sleep(3000); driver.switchTo().frame(0); userName.sendKeys("郵箱用戶名"); password.sendKeys("郵箱密碼"); loginButton.click(); Thread.sleep(5000); driver.switchTo().defaultContent(); } public WebDriver getDirver() { return driver; } }
Test126mail的源代碼如下:
public class Test126mail2 { @Test public void testLogin() throws InterruptedException { //生成一個LoginPage對象實例 LoginPage2 loginpage = new LoginPage2(); //調用登錄頁面對象的load方法,訪問被測網頁 loginpage.load(); //調用登錄頁面對象的login方法,完成登錄操作 loginpage.login(); //斷言登錄后的頁面是否包含"收件箱",來驗證是否登錄成功 //調用登錄頁面對象的getDriver方法獲取瀏覽器對象,並獲取頁面源碼 Assert.assertTrue(loginpage.getDirver().getPageSource().contains("收件箱")); //調用登錄頁面對象的quit方法關閉瀏覽器 loginpage.quit(); } }
在頁面對象中封裝了頁面元素的操作方法,使得在測試代碼中實現測試邏輯更加容易。這些封裝方法可以被很多測試邏輯重復調用,從而提高了代碼編寫和維護的效率,實現了一個類維護,很多測試類可被調用的目的,進一步降低了測試代碼的維護成本。
3.使用LoadableComponent類
繼承LoadableComponent類可以在頁面加載的時候判斷是否加載了正確的頁面,只需要重寫isLoaded和load兩個方法。此方式有助於讓頁面對象的頁面訪問操作更加健壯。
LoginPage類的源代碼:
public class LoginPage3 extends LoadableComponent<LoginPage3>{ @FindBy(name="email") public WebElement UserName; @FindBy(name="password") public WebElement password; @FindBy(id="dologin") public WebElement loginButton; public String url ="https://www.126.com/"; private String title="網易郵箱"; public WebDriver driver; //構造函數,生成瀏覽器對象,初始化PageFactory對象 public LoginPage3() { System.setProperty("webdriver.chrome.driver", "C:\\Users\\lenovo\\AppData\\Local\\Google\\Chrome\\chromedriver.exe"); driver = new ChromeDriver(); PageFactory.initElements(driver, this); } //訪問被測試網址的封裝方法 @Override public void load() { this.driver.get(url); } //關閉瀏覽器的封裝方法 public void quit() { driver.quit(); } //登錄操作的封裝方法 public void login() throws InterruptedException { Thread.sleep(3000); driver.switchTo().frame(0); UserName.sendKeys("wuyn1315667459"); password.sendKeys("goodwu"); loginButton.click(); Thread.sleep(5000); driver.switchTo().defaultContent(); } public WebDriver getDriver() { return driver; } @Override public void isLoaded() throws Error { //斷言登錄后的頁面title是否包含"網易郵箱"這幾個關鍵字 Assert.assertTrue(driver.getTitle().contains(title)); } //增加了需要覆蓋的方法load //訪問被測試網址的封裝方法 }
Test126mail類的源代碼:
public class Test126mail3 { @Test public void testLogin() throws InterruptedException { //生成一個LoginPage對象實例 LoginPage3 loginpage = new LoginPage3(); // loginpage.load(); //調用登錄頁面對象的login方法,完成登錄操作 loginpage.login(); //斷言登錄是否成功 loginpage.isLoaded(); //調用登錄頁面對象的quit方法關閉瀏覽器 loginpage.quit(); } }
4.多個PageObject的自動化測試實例
本部分主要講解多個PageObject的使用方法,以及如何基於多個PageObject實現一個相對復雜的自動化測試實例。
自動化測試實現的3個測試用例如下:
a.在126郵箱,使用正確的用戶名和錯誤的密碼進行登錄,登錄失敗並在頁面顯示“帳號或密碼錯誤”關鍵字。
b.在126郵箱,使用正確的用戶名和正確的密碼進行登錄,登錄成功后會跳轉到郵箱文件夾列表首頁,並且顯示出“收件箱”關鍵字。
c.在126郵箱,登錄成功后,單擊“寫信”鏈接,給testYY2017@126.com發送一封郵件,郵件發送成功后頁面顯示“發送成功”關鍵字。
LoginPage類:
public class LoginPage4 extends LoadableComponent<LoginPage4>{ @FindBy(name="email") public WebElement userName; @FindBy(name="password") public WebElement password; @FindBy(id="dologin") public WebElement loginButton; public String url ="https://www.126.com/"; private String title="網易郵箱"; public WebDriver driver; //構造函數,生成瀏覽器對象,初始化PageFactory對象 public LoginPage4(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); } //訪問被測試網址的封裝方法 @Override public void load() { this.driver.get(url); this.driver.manage().window().maximize(); } public void close() { this.driver.close(); } //登錄操作的封裝方法,函數方法返回一個HomePage對象 public HomePage login() throws InterruptedException { //調用load方法,瀏覽器訪問126郵箱頁面 load(); WebDriverWait wait = new WebDriverWait(driver, 10); wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("iframe:first-child"))); driver.switchTo().frame(0); //頁面判斷是否顯示了用戶名輸入框 wait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.name("email"))); //清除用戶名輸入框中的字符,保證用戶名輸入框中為空 userName.clear(); //輸入郵箱用戶名 userName.sendKeys("wuyn1315667459"); //輸入密碼 password.sendKeys("goodwu"); //單擊登錄按鈕 loginButton.click(); Thread.sleep(3000); driver.switchTo().defaultContent(); return new HomePage(driver); } //獲取頁面源碼的封裝方法 public String getPageSource() { return driver.getPageSource(); } //登錄失敗的封裝方法,函數方法返回一個LoginPage頁面對象 public LoginPage4 LoginExceptingFailure() throws InterruptedException { load(); WebDriverWait wait = new WebDriverWait(driver, 10); wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("iframe:first-child"))); driver.switchTo().frame(0); userName.sendKeys("wuyn1315667459"); password.sendKeys("goodw"); loginButton.click(); Thread.sleep(3000); //driver.switchTo().defaultContent(); //登錄失敗后,頁面不會發生跳轉,返回一個LoginPage4對象 return new LoginPage4(driver); } @Override protected void isLoaded() throws Error { // 斷言登錄后的頁面標題是否包含"網易郵箱"這幾個字 Assert.assertTrue(driver.getTitle().contains(title)); } }
HomePage類:
public class HomePage { public WebDriver driver; //寫信 @FindBy(id="_mail_component_59_59") public WebElement writeMailLink; //郵件發送按鈕 @FindBy(xpath="//*[contains(@id,'_mail_button_')]/span[contains(.,'發送')]") public WebElement sendMailButton; //收件人輸入框 @FindBy(xpath="//*[contains(@id,'_mail_emailtips')]") public WebElement receiver; public HomePage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); } //寫信的封裝方法 public SendSuccessPage writeMail(){ WebDriverWait wait = new WebDriverWait(driver, 10); wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("_mail_component_59_59"))); //單擊登錄成功后頁面上的"寫信"鏈接 writeMailLink.click(); //進入寫信頁面,等待頁面中收件人輸入框加載出來 //wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[contains(@id,'_mail_emailinput_')]/input"))); wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[contains(@id,'_mail_emailtips')]"))); //收件人輸入框獲取焦點 receiver.click(); //調用粘貼函數在收件人輸入框中粘貼 setAndCtrlVClipboardData("1315667459@qq.com"); //按tab鍵,焦點在郵件正文 pressTabKey(); //輸入郵件主題 setAndCtrlVClipboardData("郵件主題"); pressTabKey(); //輸入郵件正文 setAndCtrlVClipboardData("郵件正文"); //點擊發送郵件按鈕 sendMailButton.click(); return new SendSuccessPage(driver); } //設定剪切板病進行字符串粘貼的封裝方法 public static void setAndCtrlVClipboardData(String string) { //模擬Ctrl+V進行粘貼操作 StringSelection selection = new StringSelection(string); Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null); Robot robot = null; try { robot = new Robot(); } catch (AWTException e) { // TODO Auto-generated catch block e.printStackTrace(); } robot.keyPress(KeyEvent.VK_CONTROL); robot.keyPress(KeyEvent.VK_V); robot.keyRelease(KeyEvent.VK_CONTROL); robot.keyRelease(KeyEvent.VK_V); try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //按tab鍵的封裝方法 public static void pressTabKey() { Robot robot = null; try { robot = new Robot(); } catch (AWTException e) { // TODO Auto-generated catch block e.printStackTrace(); } robot.keyPress(KeyEvent.VK_TAB); robot.keyRelease(KeyEvent.VK_TAB); } public String getPageSource() { return driver.getPageSource(); } }
SendSuccessPage類:
public class SendSuccessPage { public WebDriver driver; public SendSuccessPage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); } public String getPageSource() { return driver.getPageSource(); } public void close() { this.driver.close(); } }
Test126mail類:
public class Test126mail4 { public WebDriver driver; @BeforeMethod public void beforeMethod() { System.setProperty("webdriver.chrome.driver", "C:\\Users\\lenovo\\AppData\\Local\\Google\\Chrome\\chromedriver.exe"); driver = new ChromeDriver(); } @AfterMethod public void afterMethod() { driver.quit(); } //測試用例登錄失敗的測試用例 @Test public void testLoginFail() throws InterruptedException { //生成一個LoginPage對象 LoginPage4 loginPage = new LoginPage4(driver); //繼承LoadableCompinent類后,只要實現覆蓋的load方法 //及時在沒有定義get方法的情況下,也可以進行get方法的調用 //get方法會默認調用頁面對象類中的load方法 //loginPage.get(); //調用LoginPage類中的登錄失敗方法 loginPage.LoginExceptingFailure(); //斷言登錄失敗后的源代碼中是否包含了'賬號或密碼錯誤'的關鍵字,調用LoginPage類中的getPageSource方法 //(注意:如果內容是在frame里,得切換到frame,才能通過PageSource獲取到) Assert.assertTrue(loginPage.getPageSource().contains("帳號或密碼錯誤")); //調用LoginPage對象中的close方法關閉瀏覽器 } //測試登錄成功的測試用例 @Test public void testLoginSuccess() throws InterruptedException { LoginPage4 loginPage = new LoginPage4(driver); //loginPage.get(); //loginPage.login()方法中已經訪問了126網頁,這邊無需再重新訪問 //調用LoginPage類中的login方法,登錄成功后會跳轉到郵箱登錄后主頁 //login函數會返回一個HomePage對象,以此來實現頁面跳轉到登錄后主頁 //以便實現在HomePage對象中進行相關的方法調用 HomePage homePage = loginPage.login(); //等待5秒,等待從登錄頁面跳轉到郵箱登錄后主頁 Thread.sleep(5000); //斷言登錄成功后的源代碼是否包含了“收件箱”的關鍵字,來驗證是否登錄成功 Assert.assertTrue(homePage.getPageSource().contains("收件箱")); loginPage.close(); } //測試發送郵件成功的測試用例 @Test public void testwriteMail() throws InterruptedException { LoginPage4 loginPage = new LoginPage4(driver); HomePage homePage = loginPage.login(); //等待5秒,等待郵件發送完成 Thread.sleep(5000); //調用寫郵件方法,完成在頁面上的發送郵件操作 SendSuccessPage successPage = homePage.writeMail(); //等到五秒,等待郵件發送成功 Thread.sleep(5000); //斷言郵件發送后,是否出二線"發送成功"4個字,以此驗證郵件是否發送成功 Assert.assertTrue(successPage.getPageSource().contains("發送成功")); successPage.close(); } }
遇見的問題:
1.登錄錯誤用例中,斷言頁面是否包含“帳號或密碼錯誤”,報錯元素不存在
原因:該元素在iframe中必須切換進入iframe中,pageSource方法才能包含該元素
2.發送郵件成功用例中,定位收件人輸入框,使用xpath定位://*[contains(@id,'_mail_emailinput_')]/input" ,報錯,元素不可點擊
unknown error: Element <input class="nui-editableAddr-ipt" type="text" role="combobox" tabindex="1" aria-label="收件人地址輸入框,請輸入郵件地址,多人時地址請以分號隔開"> is not clickable at point (125, 163). Other element would receive the click: <label id="_mail_emailtips_0_214" class="js-component-emailtips nui-ipt-placeholder">...</label>
使用覆蓋元素,可以實現功能
設計原則
(1)在PageObject類中定義public方法來對外提供服務。
(2)不要暴露PageObject類中的內部邏輯。
(3)不要在PageObject類中進行斷言操作。
(4)只需要在PageObject類中定義需要操作的元素和操作方法。
(5)PageObject頁面中的相同動作如果會產生多個不同的結果,需要在PageObject類中定義多個操作方法。