浅谈pageobject模式
先来看两段代码
代码1:
1 package com.zlshuo.selenium.nonaming.pageobject; 2 3 /** 4 * @author leshuo 5 * @version 2014年5月14日 6 */ 7 public class AT { 8 9 public static void main(String[] args){ 10 11 //获取二手房经纪人电话 12 String telephoneNum=BasePage.get("http://shanghai.anjuke.com"). 13 serachKeyWords("上海别墅").clickPorpByindex(1).getTelephoneNum(); 14 15 System.out.println(telephoneNum); 16 17 //退出driver 18 BasePage.quit(); 19 20 } 21 }
代码2:
1 package com.zlshuo.selenium.nonaming.pageobject; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 import java.util.Set; 6 import org.openqa.selenium.By; 7 import org.openqa.selenium.NoSuchWindowException; 8 import org.openqa.selenium.WebDriver; 9 import org.openqa.selenium.chrome.ChromeDriver; 10 import org.testng.Assert; 11 12 /** 13 * @author leshuo 14 * @version 2014年5月14日 15 */ 16 public class AT1 { 17 18 private static WebDriver driver; 19 public static void main(String[] args){ 20 21 System.setProperty ( "webdriver.chrome.driver" , 22 "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe" ); 23 WebDriver driver = new ChromeDriver(); 24 driver.get("http://shanghai.anjuke.com"); 25 driver.findElement(By.xpath("//input[@id='glb_search0']")).sendKeys("上海别墅"); 26 driver.findElement(By.xpath("//input[@id='glb_search0']")).submit(); 27 driver.findElement(By.xpath("//a[@id='prop_name_qt_prop_1']")).click(); 28 driver=switchToWindow(2); 29 String telephoneNum=driver. 30 findElement(By.xpath("//div[@class='A-fangyuan-call']/p[@class='number']")). 31 getText(); 32 System.out.println(telephoneNum); 33 driver.quit(); 34 35 } 36 37 public static List<String> getWindowHandlesList() { 38 Set<String> handles = driver.getWindowHandles(); 39 List<String> list=new ArrayList<String>(); 40 for (String s : handles){ 41 list.add(s); 42 } 43 return list; 44 } 45 46 public static WebDriver switchToWindow(int index) { 47 try { 48 int windowNumber=getWindowHandlesList().size(); 49 if(windowNumber<=0){ 50 Assert.fail("窗口数为0"); 51 }else if(windowNumber==1) 52 ; 53 else{ 54 return driver.switchTo().window(getWindowHandlesList().get(index-1)); 55 } 56 57 } catch (NoSuchWindowException e) { 58 e.printStackTrace(); 59 } 60 return driver; 61 62 } 63 }
这两段代码完成的功能都是一样的:访问安居客搜索"上海别墅",点击搜索结果的第一套房源,获取经纪人的手机号码。
代码1看上去要简洁很多,采用pageobject模式,便于单个用例脚本的编写展示,代码2是用原生的selenium书写,高下立判。代码1用下面这个版本会更加容易理解一点
1 package com.zlshuo.selenium.nonaming.pageobject; 2 3 /** 4 * @author leshuo 5 * @version 2014年5月14日 6 */ 7 public class AT { 8 9 public static void main(String[] args){ 10 11 //访问安居客首页 12 AnjukeHomePage homePage=BasePage.get("http://shanghai.anjuke.com"); 13 //搜索上海别墅二手房 14 AnjukeSaleListPage listPage=homePage.serachKeyWords("上海别墅"); 15 //点击第一套房源 16 AnjukeSalePropPage propPage=listPage.clickPorpByindex(1); 17 //获取房源单页经纪人电话 18 String telephoneNum=propPage.getTelephoneNum(); 19 20 System.out.println(telephoneNum); 21 22 //退出driver 23 BasePage.quit(); 24 25 } 26 }
先来看看BasePage,BasePage是所有页面的父类,其他页面需要继承之,他有两个方法get和quit.get用来初始化driver并访问url,quit用来退出driver,kill浏览器进程
1 package com.zlshuo.selenium.nonaming.pageobject; 2 3 import org.openqa.selenium.WebDriver; 4 import org.openqa.selenium.chrome.ChromeDriver; 5 6 /** 7 * @author leshuo 8 * @version 2014年5月14日 9 */ 10 public class BasePage { 11 12 private static WebDriver driver; 13 14 public static AnjukeHomePage get(String url){ 15 System.setProperty ( "webdriver.chrome.driver" , 16 "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe" ); 17 driver = new ChromeDriver(); 18 driver.get(url); 19 return new AnjukeHomePage(driver); 20 } 21 22 public static void quit(){ 23 driver.quit(); 24 } 25 26 }
AnjukeHomePage是BasePage.get()返回的页面对象,这里定义为安居客首页,他有一个serachKeyWords方法,用于搜索关键词并返回列表页面
1 package com.zlshuo.selenium.nonaming.pageobject; 2 3 import org.openqa.selenium.By; 4 import org.openqa.selenium.WebDriver; 5 6 /** 7 * @author leshuo 8 * @version 2014年5月14日 9 */ 10 public class AnjukeHomePage extends BasePage{ 11 12 private WebDriver driver; 13 14 AnjukeHomePage(WebDriver driver){ 15 this.driver=driver; 16 } 17 18 public AnjukeSaleListPage serachKeyWords(String words){ 19 driver.findElement(By.xpath("//input[@id='glb_search0']")).sendKeys(words); 20 driver.findElement(By.xpath("//input[@id='glb_search0']")).submit(); 21 return new AnjukeSaleListPage(driver); 22 } 23 24 }
AnjukeSaleListPage是搜索列表页,此页面展示了搜索出的相关房源信息,我只写了一个选择房源的方法作为例子,点击房源后就进入了房源显示的页面了,所以clickPorpByindex返回的是房源单页对象AnjukeSalePropPage
1 package com.zlshuo.selenium.nonaming.pageobject; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 import java.util.Set; 6 7 import org.openqa.selenium.By; 8 import org.openqa.selenium.NoSuchWindowException; 9 import org.openqa.selenium.WebDriver; 10 import org.testng.Assert; 11 12 /** 13 * @author leshuo 14 * @version 2014年5月14日 15 */ 16 public class AnjukeSaleListPage extends BasePage{ 17 18 private WebDriver driver; 19 20 public AnjukeSaleListPage(WebDriver driver){ 21 this.driver=driver; 22 } 23 24 public AnjukeSalePropPage clickPorpByindex(int index){ 25 driver.findElement(By.xpath("//a[@id='prop_name_qt_prop_"+index+"']")).click(); 26 //切换到新开的窗口 27 driver=switchToWindow(2); 28 return new AnjukeSalePropPage(driver); 29 } 30 31 private List<String> getWindowHandlesList() { 32 Set<String> handles = driver.getWindowHandles(); 33 List<String> list=new ArrayList<String>(); 34 for (String s : handles){ 35 list.add(s); 36 } 37 return list; 38 } 39 40 private WebDriver switchToWindow(int index) { 41 try { 42 int windowNumber=getWindowHandlesList().size(); 43 if(windowNumber<=0){ 44 Assert.fail("窗口数为0"); 45 }else if(windowNumber==1) 46 ; 47 else{ 48 return driver.switchTo().window(getWindowHandlesList().get(index-1)); 49 } 50 51 } catch (NoSuchWindowException e) { 52 e.printStackTrace(); 53 } 54 return driver; 55 56 } 57 }
AnjukeSalePropPage只有一个方法即获取经纪人电话
1 package com.zlshuo.selenium.nonaming.pageobject; 2 3 import org.openqa.selenium.By; 4 import org.openqa.selenium.WebDriver; 5 6 /** 7 * @author leshuo 8 * @version 2014年5月14日 9 */ 10 public class AnjukeSalePropPage extends BasePage{ 11 12 private WebDriver driver; 13 14 public AnjukeSalePropPage(WebDriver driver){ 15 this.driver=driver; 16 } 17 18 public String getTelephoneNum(){ 19 return driver.findElement(By.xpath("//div[@class='A-fangyuan-call']/p[@class='number']")). 20 getText(); 21 } 22 }
不难看出,pageobject模式以业务逻辑上的每一步操作作为区分点,页面方法代表了此页面的一个业务操作并严格控制此操作的后续流程,这样做的好处有以下几点:
1.以页面为单位,重复的操作封装在一个方法中,当页面元素或流程变动时只需修改相关页面方法即可,不需要修改相应的脚本
2.测试脚本编写简单,顺着业务逻辑写脚本和手工测试一样的感觉。例如,访问安居客首页->搜索上海别墅->点击搜索结果的第一套房源进入房源单页->获取经纪人手机号码,写成脚本就是BasePage.get("http://shanghai.anjuke.com").serachKeyWords("上海别墅").clickPorpByindex(1).getTelephoneNum(),一气呵成。
3.可以和PageFactory模式一起使用,节约前期开发成本
当然,这些奇淫巧计都是锦上添花之物,真正的着落之处应该在规范流程及提高开发者个人水平这一方面。
先污染后治理的理念是要摒弃的。