PageObjects 设计模式
什么是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它们实现了什么交互功能,而不是其内部的实现,因此,这里的功能与开发人员理解的功能是不一样的。
以用户登录为例:在登录界面,点击登录后要么成功,转向首页。要么失败,出现提示出错信息。
相信这是一个很容易理解的场景吧!
JavaCode可能类似如下:
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字段并不一定唯一,并且javaClass的属性看起来都是一堆无意义的名称。这些要求我们必须要进行改进。幸好PageFactory已经提前考虑到了这一切,它支持annotations来显式定位元素。那么上述的百度搜索样例,可以修改为如下形式:
public class BaiduSearchPage {
public static final Logger LOG = LoggerFactory
.getLogger(BaiduSearchPage.class);
@FindBy(how = How.NAME, using = "wd")
@CacheLookup
private 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使用,将使你的自动化测试优雅、易懂、易维护。
PageObjects 设计模式的更多相关文章
- 如何使用Pytest进行自动化测试
为什么需要自动化测试 自动化测试有很多优点,但这里有3个主要的点 可重用性:不需要总是编写新的脚本,除非必要,即使是新的操作系统版本也不需要编写脚本. 可靠性:人容易出错,机器不太可能.当运行不能跳过 ...
- [小北De编程手记] [Lesson 02] AutoFramework构建 之 Page Objects - 设计模式
写在最前面 这个系列的主旨是要跟大家分享一下关于自动化测试框架的构建的一些心得.这几年,做了一些自动化测试框架以及团队的构建的工作.过程中遇到了很多这样的同学,他们在学习了某一门语言和一些自动化测试的 ...
- 基于Python Selenium Unittest PO设计模式详解
本文章会讲述以下几个内容: 1.什么是PO设计模式(Page Object Model) 2.为什么要使用PO设计模式 3.使用PO设计模式要点 4.PO设计模式实例 1.什么是PO设计模式 (Pag ...
- Page Object设计模式(一)
一.简介 主要特点体现在“对界面交互细节的封装”上,使测试用例更专注于业务的操作,从而提高测试用例的可维护性.解决UI变动问题. page对象的一个基本原则经验法则是:凡是人能做的事,page对象通过 ...
- 《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
1.简介 上一篇介绍了POM的基础理论知识和非POM方式写脚本,这篇介绍利用页面工厂类(page factory)去实现POM,通过查看PageFactory类,我们可以知道它是一个初始化一个页面实例 ...
- 《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
1.简介 上一篇宏哥用PageFactory实现了POM,宏哥再介绍一下如果不用PageFactory如何实现POM. 2.项目实战 在这里宏哥以百度首页登录的例子,如果用POM实现,在测试脚本中实际 ...
- 五分钟搞懂POM设计模式
转载请注明出处️ 作者:IT小学生蔡坨坨 原文链接:五分钟搞懂POM设计模式 大家好,我是IT小学生蔡坨坨. 今天,我们来聊聊Web UI自动化测试中的POM设计模式. 为什么要用POM设计模式 前期 ...
- MVVM设计模式和WPF中的实现(四)事件绑定
MVVM设计模式和在WPF中的实现(四) 事件绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...
- java EE设计模式简介
1.何为设计模式 设计模式提供了对常见应用设计问题的解决方案.在面向对象的编程中,设计模式通常在解决与对象创建和交互相关的问题,而非整体软件架构所面对的大规模问题,它们以样板代码的形式提供了通用的解决 ...
随机推荐
- hbase java Api练习
package cn.itcast_01_hbase; import java.util.ArrayList; import java.util.List; import org.apache.had ...
- Unix系统编程(四)creat系统调用
我好疑惑啊,creat系统调用为啥没有以e结尾呢?搞得我每次都怀疑我敲错了. 在早期的UNIX实现中,open只有两个参数,无法创建新文件,而是使用creat系统调用创建并打开一个新文件. int c ...
- 【转帖】关于sql server 2008 的mdf收缩问题
原帖地址:http://social.msdn.microsoft.com/forums/windowsazure/pt-br/388f92e1-9a1e-497d-bde1-6664561fd44e ...
- CI框架中 日志输出方法log_message()只允许输出字符串解决方案
1.修改CodeIgniter/index.php,添加如下: define('ROOTDIR',dirname(__FILE__).'/'); 2.修改CodeIgniter/application ...
- C++ 类的继承六(多继承的二义性--虚基类)
//多继承的二义性--虚基类(了解为主) #include<iostream> using namespace std; /* 多继承在现在的项目开发中一般不使用,他会增加项目的复杂度 * ...
- 第二百三十九节,Bootstrap路径分页标签和徽章组件
Bootstrap路径分页标签和徽章组件 学习要点: 1.路径组件 2.分页组件 3.标签组件 4.徽章组件 本节课我们主要学习一下 Bootstrap 的四个组件功能:路径组件.分页组件.标签组件 ...
- js和jquery获取父级元素、子级元素、兄弟元素的方法{转}
先说一下JS的获取方法,其要比JQUERY的方法麻烦很多,后面以JQUERY的方法作对比 JS的方法会比JQUERY麻烦很多,主要则是因为FF浏览器,FF浏览器会把你的换行也当成DOM元素 原生的 ...
- Hadoop2.0中单点故障解决方案分析
Hadoop 1.0内核主要由两个分支组成:MapReduce和HDFS,众所周知,这两个系统的设计缺陷是单点故障,即MR的JobTracker和HDFS的NameNode两个核心服务均存在单点问题, ...
- src与href的异同
相同点: 在跨域中,src,href,这些发送的请求都是get请求: 不同点: 1, 概念:href (Hypertext Reference)指定网络资源的位置: 理解:href 用作 " ...
- hiho一下第107周《Give My Text Back》
题目链接:传送门 题目大意:给你多组格式不标准的字符串句子,要你恢复到标准格式(单词中间只有一个空格或者一个逗号一个空格,句子的首字母大写其他小写,遇到回车也要换行) 题目思路:直接按题意模拟,注意几 ...