什么是Page Objects(翻译为:页面对象?)…

简单的说,Page Objects是指UI界面上用于与用户进行交互的对象。它可以指整个页面,也可以指Page上的某个区域。Page Objects是你的test code的交互对象,是对实际UI的一种抽象模型化。通过Page Objects可以减少重复代码的编写,例如,很多页面都有同样的header,footer,navigator等部分,如果对这些进行抽象,只写一次就可以在其他地方通用了。

注意PageObjects与Page Objects是不一样的,PageObjects用于特指采用Page Objects进行封装的一种设计模式(Design Pattern),而不仅仅是多一个空格的区别。哈。

如何实现PageObjects设计模式?

一般情况下,对于一个Page Objects对象,它有两个方面的特征:

  • 自身元素(WebElement)
  • 实现功能 (Services)

自身元素很好理解,就是实实在在的页面元素。而Page Object通常也都是实现一定的功能的。就Test的开发人员来说,更关心的是Page Objects它们实现了什么交互功能,而不是其内部的实现,因此,这里的功能与开发人员理解的功能是不一样的

以用户登录为例:在登录界面,点击登录后要么成功,转向首页。要么失败,出现提示出错信息。 
相信这是一个很容易理解的场景吧!
Java Code可能类似如下:

public class LoginPage {//用户名录入框private WebElement usernameBox;//密码录入框private WebElement passwordBox;//提交按钮private WebElement submitButton;public HomePage loginAs(String username, String password) {usernameBox.sendKeys(username);passwordBox.sendKeys(password);submitButton.submit();return new HomePage(...)}public LoginPage loginAsExpectingError(String username, String password) {//  出错的username,password 仍留在LoginPage}public String getErrorMessage() {// 获取错误信息}
}

从上面可以看出,同时封装了元素以及功能。此处样例,元素是没有初始化的。可以通过类似于driver.findElement()的函数来直接进行初始化,另外WebDriver提供了一个PageFactory用于对PageObjects设计模式进行支持,下面将会讲到。
通过上面的这段代码,也展现出了一个重要的问题,那就是assertion不应该在Page Objects内部,而应该由tests进行处理。Page Objects只是返回需要验证的信息即可。

总结

  • public方法代表Page提供的功能
  • 尽量不要暴露Page的内部细节
  • 不要assertion
  • 方法可以返回其他Page Objects
  • Page Objects不用代表整个页面,可以是任意一个部分
  • 一样的操作,不同的结果应该分开(正确登录,错误登录)

样例

public class LoginPage {private final WebDriver driver;// 用户名录入框private WebElement usernameBox;// 密码录入框private WebElement passwordBox;// 提交按钮private WebElement submitButton;public LoginPage(WebDriver driver) {this.driver = driver;if (!"Login".equals(driver.getTitle())) {throw new IllegalStateException("This is not the login page");}this.usernameBox = driver.findElement(By.id("username"));this.passwordBox = driver.findElement(By.id("passwd"));this.submitButton = driver.findElement(By.id("login"));}public HomePage loginAs(String username, String password) {usernameBox.sendKeys(username);passwordBox.sendKeys(password);submitButton.submit();return new HomePage(driver);}
}

PageFactory

从上面的样例中,有没有发现每个元素都要进行driver.findElement()这样的操作,写起来好累啊,一堆重复性的代码。有没有更好的,更优雅的处理方法呢?org.openqa.selenium.support.PageFactory就是用来负责处理这个的,真Happy! 
下面以百度搜索作为样例场景,搜索一个关键字:

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.support.PageFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/**
* @author shenyanchao
* */
public class BaiduSearchPage {public static final Logger LOG = LoggerFactory.getLogger(BaiduSearchPage.class);private WebElement wd;public void searchFor(String keyword) {wd.sendKeys(keyword);wd.submit();}public static void main(String[] args) {WebDriver driver = new HtmlUnitDriver();driver.get("http://www.baidu.com");BaiduSearchPage baiduPage = PageFactory.initElements(driver,BaiduSearchPage.class);LOG.info("before search url is:{}",driver.getCurrentUrl());baiduPage.searchFor("blueshen");LOG.info("after search url is:{}",driver.getCurrentUrl());}
}

运行以上代码,发现已经可以正常运行,结果如下:

......
before search url is:http://www.baidu.com/
......
after search url is:http://www.baidu.com/s?wd=blueshen&rsv_bp=0&rsv_spt=3

可见,搜索后,已经转向了正确的搜索结果页面。然而WebElement是如何初始化的呢?玄机就在BaiduSearchPage baiduPage = PageFactory.initElements(driver,BaiduSearchPage.class);这行代码。PageFactory负责初始化了Page里的元素,amazing,用起来就是这么的优雅。
那么下来,我就要问了:PageFactory是怎么定位元素的呢?

原来PageFactory初始化元素有一个惯例,样例中将WebElement的名称定为wd,那么PageFactory将按类似以下的形式对其进行初始化: 
driver.findElement(By.id("wd"));
PageFactory认为wd是HTML元素的id或者name字段的值,并且优先从id开始查找。至此,我们终于知道怎么回事了。

随着项目的变大,以及使用的更加深入,HTML元素的id,name字段并不一定唯一,并且java Class的属性看起来都是一堆无意义的名称。这些要求我们必须要进行改进。幸好PageFactory已经提前考虑到了这一切,它支持annotations来显式定位元素。那么上述的百度搜索样例,可以修改为如下形式:

public class BaiduSearchPage {public static final Logger LOG = LoggerFactory.getLogger(BaiduSearchPage.class);@FindBy(how = How.NAME, using = "wd")@CacheLookupprivate WebElement serachBox;public void searchFor(String keyword) {serachBox.sendKeys(keyword);serachBox.submit();}
......
}

明确的指定HOW.NAME,using=”wd”,意为查找name=”wd”的元素,并将其初始化赋值给searchBox这一有意义的属性名。其中@CacheLookup用于标识其只初始化一次,然后缓存起来备用。

感觉还不够简洁吗?继续修改:

@FindBy(name = "wd")
private WebElement searchBox;

这是其简略模式,还支持各种定位方式。

    @FindBy(id="...")@FindBy(className="...")@FindBy(name="...")@FindBy(xpath="...")@FindBy(linkText="...")@FindBy(partialLinkText="...")@FindBy(tagName="...")@FindBy(css="...")

同时支持@FindBys用于支持列表元素查找定位,返回List<WebElement>类型。

总之,利用PageObjects设计模式并且配合PageFactory使用,将使你的自动化测试优雅、易懂、易维护。

转载于:https://www.cnblogs.com/111testing/p/7198372.html

PageObjects 设计模式相关推荐

  1. 什么是Page Objects(PageObjects 设计模式)

    什么是Page Objects(翻译为:页面对象?) 写在最前面,感谢超爷的这个文档.  2012-10-17你完成的,我到了14年的5月才回过神来整理这部分知识. 部分内容来自:https://co ...

  2. 如何使用Pytest进行自动化测试

    为什么需要自动化测试 自动化测试有很多优点,但这里有3个主要的点 可重用性:不需要总是编写新的脚本,除非必要,即使是新的操作系统版本也不需要编写脚本. 可靠性:人容易出错,机器不太可能.当运行不能跳过 ...

  3. [小北De编程手记] [Lesson 02] AutoFramework构建 之 Page Objects - 设计模式

    写在最前面 这个系列的主旨是要跟大家分享一下关于自动化测试框架的构建的一些心得.这几年,做了一些自动化测试框架以及团队的构建的工作.过程中遇到了很多这样的同学,他们在学习了某一门语言和一些自动化测试的 ...

  4. 【Design pattern】设计模式思路总结(一)

    看了一周的设计模式,跟着小菜的思路走! 从简单工厂过渡策略,后面看的这几个模式都是在单一职责,开放--封闭原则,依赖倒转原则下不断的改进,采用模式写出的代码更容易扩展,维护! 比较容易懂. 装饰模式: ...

  5. GOF23设计模式(结构型模式)代理模式~

    代理模式应用场景十分广泛,随便一个框架都会用到,因此学好代理模式对后续框架学习是最基本的要素!!今天我们就来讲讲代理模式! 目录 1.简介 1. 核心作用 2. 角色分析 2. 应用场景 4. 分类 ...

  6. GOF23设计模式(创建型模式)工厂模式

    目录: 一:工厂模式的核心本质 二:关于面向对象的六大基本原则 三:工厂模式的三大类详解(代码示例,详细分析) 首先,上咱本GOF23所有工厂模式的分类表格!!! 创建型模式 单例模式.工厂模式.抽象 ...

  7. GOF23设计模式(创建型模式)单例模式

    目录: 一:单例模式的核心作用.常见应用场景 二:五种单例模式及其实现 三:关于反射和反序列化破解单例模式的漏洞,以及相应的解决方案 四:测试五种单例模式的效率 一:核心作用及常见应用场景: 核心作用 ...

  8. Python七大原则,24种设计模式

    七大设计原则: 1.单一职责原则[SINGLE RESPONSIBILITY PRINCIPLE]:一个类负责一项职责.  2.里氏替换原则[LISKOV SUBSTITUTION PRINCIPLE ...

  9. Java设计模式:单例模式

    学而时习,稳固而之心, 好久没有复习java的知识了,今天有空温习了单例模式,这里记录一下 单例模式是常见的设计模式的一种,其特点就是 指一个类只有一个实例,且该类能自行创建这个实例  , 保证一个类 ...

最新文章

  1. 用Selenium自动化测试时,让ChromeDriver中不显示“正受到自动测试软件控制”
  2. IDEA开发中常用快捷键
  3. 回归网易 9 个月来的测试团队转型之路
  4. java多表查询实体类_自己设计一个JAVA+MyBatis解析实体类多表通用查询
  5. 用Adobe Flex3开发AIR应用程序–入门指南
  6. linux系统下字体目录,Linux CentOS 7 安装字体库 中文字体
  7. 幼儿抽象逻辑思维举例_3-6岁,抓住幼儿思维发展关键期,培养孩子逻辑思维能力...
  8. 如何看懂常用原理图符号、如何阅读原理图
  9. 《回话的技术》读后感
  10. postgresql 优势
  11. C#中的Obsolete特性
  12. 谷歌云端硬盘下载文件_如何为Google云端硬盘上的文件创建共享下载链接
  13. python提取发票信息发票识别_分享一个电子发票信息提取工具(Python)
  14. 软件测试需要具备的技能(软技能硬技能)
  15. python百度云ocr文字识别软件_基于百度云的OCR识别(Python)
  16. Python zip*用法(一行代码实现矩阵的旋转)
  17. bash和shell_Bash和Shell扩展:懒惰的列表制作
  18. mysql联合索引怎么存储_联合索引在B+树上的存储结构及数据查找方式
  19. echars 字符云效果,echars字符云颜色问题
  20. Java IDE使用基础

热门文章

  1. 广联达文件被锁怎么修复_事无巨细的绕过Apple id教程+修复蜂窝数据/电话
  2. mongodb python 大于_Python中使用MongoDB详解
  3. 【Pytorch神经网络理论篇】 10 优化器模块+退化学习率
  4. 网络教育计算机 判断,北京师范大学网络教育计算机作业1、4、8
  5. 笔记本AutoCAD启动时闪退怎么办_戴尔笔记本电脑开不了机如何解决【解决方法】...
  6. LeetCode 492. 构造矩形
  7. POJ 2287 田忌赛马(贪心)
  8. qr分解求线性方程组_计算方法/数值分析第三章 线性方程组的数值解法
  9. ifix的MySQL数据库_iFIX 技术文章:iFIX历史数据库
  10. 路由器距离向量算法计算举例_路由器基本原理是什么 路由器基本发展情况介绍【图文】...