selenium目前比较流行的设计模式就是page object,那么到底什么是page object呢,简单来说,就是把页面作为对象,在使用中传递页面对象,来使用页面对象中相应的成员或者方法,能更好的提现java的面向对象和封装特性,首先看一下官网的解释:
Page Object Design Pattern
Page Object is a Design Pattern which has become popular in test automation for enhancing test maintenance and reducing code duplication. A page object is an object-oriented class that serves as an interface to a page of your AUT. The tests then use the methods of this page object class whenever they need to interact with the UI of that page. The benefit is that if the UI changes for the page, the tests themselves don’t need to change, only the code within the page object needs to change. Subsequently all changes to support that new UI are located in one place.
The Page Object Design Pattern provides the following advantages
1. There is a clean separation between test code and page specific code such as locators (or their use if you’re using a UI Map) and layout.
2. There is a single repository for the services or operations offered by the page rather than having these services scattered throughout the tests.
In both cases this allows any modifications required due to UI changes to all be made in one place. Useful information on this technique can be found on numerous blogs as this ‘test design pattern’ is becoming widely used. We encourage the reader who wishes to know more to search the internet for blogs on this subject. Many have written on this design pattern and can provide useful tips beyond the scope of this user guide. To get you started, though, we’ll illustrate page objects with a simple example.
First, consider an example, typical of test automation, that does not use a page object.
/*** * Tests login feature */ public class Login { public void testLogin() { selenium.type("inputBox", "testUser"); selenium.type("password", "my supersecret password"); selenium.click("sign-in"); selenium.waitForPageToLoad("PageWaitPeriod"); Assert.assertTrue(selenium.isElementPresent("compose button"), "Login was unsuccessful"); } }
There are two problems with this approach.
- There is no separation between the test method and the AUT’s locators (IDs in this example); both are intertwined in a single method. If the AUT’s UI changes its identifiers, layout, or how a login is input and processed, the test itself must change.
- The ID-locators would be spread in multiple tests, in all tests that had to use this login page.
Applying the page object techniques, this example could be rewritten like this in the following example of a page object for a Sign-in page.
/** * Page Object encapsulates the Sign-in page. */ public class SignInPage { private Selenium selenium; public SignInPage(Selenium selenium) { this.selenium = selenium; if(!selenium.getTitle().equals("Sign in page")) { throw new IllegalStateException("This is not sign in page, current page is: " +selenium.getLocation()); } } /** * Login as valid user * * @param userName * @param password * @return HomePage object */ public HomePage loginValidUser(String userName, String password) { selenium.type("usernamefield", userName); selenium.type("passwordfield", password); selenium.click("sign-in"); selenium.waitForPageToLoad("waitPeriod"); return new HomePage(selenium); } }
and page object for a Home page could look like this.
/** * Page Object encapsulates the Home Page */ public class HomePage { private Selenium selenium; public HomePage(Selenium selenium) { if (!selenium.getTitle().equals("Home Page of logged in user")) { throw new IllegalStateException("This is not Home Page of logged in user, current page" + "is: " +selenium.getLocation()); } } public HomePage manageProfile() { // Page encapsulation to manage profile functionality return new HomePage(selenium); } /*More methods offering the services represented by Home Page of Logged User. These methods in turn might return more Page Objects for example click on Compose mail button could return ComposeMail class object*/ }
So now, the login test would use these two page objects as follows.
/*** * Tests login feature */ public class TestLogin { public void testLogin() { SignInPage signInPage = new SignInPage(selenium); HomePage homePage = signInPage.loginValidUser("userName", "password"); Assert.assertTrue(selenium.isElementPresent("compose button"), "Login was unsuccessful"); } }
官网中使用的代码例子是selenium1的方式,为了方便我使用webdirver做了实现,并使用知乎登录作为例子。
思路:
A:打开浏览器
B:访问www.zhihu.com
C:登录
D:登录后,主页验证是否登录成功
以上四个步骤用到了2个页面,登录页面和主页,下面我们就对这两个页面进行封装。
1.新建一个HomePage类。
package com.pageobject1; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.testng.Assert; /** * @author QiaoJiafei * @version 创建时间:2016年2月26日 上午10:01:51 * 类说明 */ public class HomePage { private WebDriver driver; WebElement element_account; public HomePage(WebDriver driver) { if(!driver.getTitle().equals("首页 - 知乎")) { throw new IllegalStateException("This is not Home Page of logged in user, current page" + "is: " +driver.getTitle()); } element_account = driver.findElement(By.id(":0")); } public HomePage manageProfile() { // Page encapsulation to manage profile functionality String text = element_account.getText(); Assert.assertEquals(text, "乔叶叶"); return new HomePage(driver); }
public XXXpage clickhrep() {
//点击某个链接的方法,并返回跳转后的页面对象
return XXXpage(driver);
} }
2.新建一个LoginPage类
package com.pageobject1; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.WebDriverWait; import com.google.common.base.Predicate; /** * @author QiaoJiafei * @version 创建时间:2016年2月26日 上午9:55:23 * 类说明 */ public class LoginPage { private WebDriver driver; WebElement element_username; WebElement element_password; WebElement element_submit; public LoginPage(WebDriver driver) { // TODO Auto-generated constructor stub this.driver = driver; if(!driver.getTitle().equals("知乎 - 与世界分享你的知识、经验和见解")) { throw new IllegalStateException("This is not sign in page, current page is: " +driver.getTitle()); } element_username = driver.findElement(By.name("account")); element_password = driver.findElement(By.name("password")); element_submit = driver.findElement(By.xpath("/html/body/div[1]/div/div[2]/div[2]/form/div[3]/button")); } public HomePage loginValidUser(String userName, String password) { element_username.sendKeys(userName); element_password.sendKeys(password); element_submit.click(); WebDriverWait wait = new WebDriverWait(driver, 10); wait.until(new ExpectedCondition<WebElement>(){ @Override public WebElement apply(WebDriver arg0) { // TODO Auto-generated method stub return arg0.findElement(By.id(":0")); } }).isDisplayed();
//注意根据实际业务,返回相应页面对象 return new HomePage(driver); } }
3.新建一个测试类
package com.pageobject1; import java.sql.Driver; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * @author QiaoJiafei * @version 创建时间:2016年2月26日 上午10:20:29 * 类说明 */ public class TestLogin { WebDriver driver = null; @BeforeClass public void beforeClass() { String key = "webdriver.firefox.bin"; String value = "C:/Program Files (x86)/Mozilla Firefox/firefox.exe"; System.setProperty(key, value); driver = new FirefoxDriver(); driver.manage().window().maximize(); driver.get("https://www.zhihu.com/#signin"); } @AfterClass public void afterClass() { driver.quit(); } @Test public void testLogin() { LoginPage loginPage = new LoginPage(driver); HomePage homePage = loginPage.loginValidUser("XXXXX@163.com", "XXXXX"); /* * 下面就可以使用homepage对象来做操作,如: * homePage.manageProfile(); * * */ Assert.assertEquals(driver.getTitle(), "首页 - 知乎"); } }
由此可见,在测试类中,操作的基本都是页面对象,逻辑清晰,代码简单,在后期的维护中,如果那个页面的元素发生变化,则只需要更改相应页的页面类即可,不需要更改测试类的逻辑。
看到这里有的也许会问:每次都要求返回一个页面对象,有时不确定怎么办?
我的解决办法是:在实际的自动化测试中,是先有测试用例,后编写测试脚本,所以基本每个页面传递对象都可以确定,如在主页我点击一篇文章会跳转一个文章页面,点击个人头像灰跳转一个个人页面,在就是在主页类中写两个方法,一个方法返回文章页面,一个方法返回个人页面,依次类推……
好了,这篇文件就先简单介绍一下page object的设计思想及用法,以后会写一篇文章,如何在框架中使用这种模式