Selenium(十九):unittest单元测试框架(五) Page Object设计模式
1. Page Object设计模式
Page Object是Selenium自动化测试项目开发实践的最佳设计模式之一,它主要体现在对界面交互细节的封装,这样可以使测试方案更关注于业务而非界面细节。从而提高测试案例的可读性。
1.1 认识Page Object
Page Object设计模式的优点如下:
减少代码的重复
提高测试用例的可读性
提高测试用例的可维护性,特别是针对UI频繁变化的项目。
当为Web页面编写测试时,需要操作该Web页面上的元素。然而,如果在测试代码中直接操作HTML元素,那么你的代码是及其脆弱,因为UI经常变动。我们可以将一个page对象封装成一个HTML页面,然后通过提供的应用程序特定的API来操作页面元素,而不是在HTML中四处搜寻。
Page Object原理:

page对象的一个基本经验法则是:凡是人能做的是,page对象通过软件客户端都能够做到。因此,它也应当提供一个易于编程的接口并隐藏窗口中底层的部分。所以访问一个文本框应该通过一个访问方法(accessor method)来实现字符串的获取与返回,复选框应当使用布尔值,按钮应当被表示为行为导向的方法名。page对象应当将在GUI控件上所以查询和操作数据的行为封装为方法。一个好的经验法则是,即使改变具体的控件,page对象的接口也不应当发生变化。
尽管该术语是“页面”对象,但并不意味着想要针对每个页面建立一个这样的对象,例如,页面有重要意义的元素可以独立为一个page对象。经验法则的目的是通过给页面建模,使其对应用程序的使用者变得有意义。
1.2 Page Object实例
下面以登录126邮箱为例,通过Page Object设计模式来实现。
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep #创建基础类
class BasePage(object):
#初始化
def __init__(self, driver):
self.base_url = 'https://mail.qq.com/'
self.driver = driver
self.timeout = 30 #定义打开登录页面方法
def _open(self):
url = self.base_url
self.driver.get(url)
self.driver.switch_to.frame('login_frame') #切换到登录窗口的iframe #定义定义open方法,调用_open()进行打开
def open(self):
self._open() #定位方法封装
def find_element(self,*loc):
return self.driver.find_element(*loc) #创建LoginPage类
class LoginPage(BasePage):
username_loc = (By.ID, "u")
password_loc = (By.ID, "p")
login_loc = (By.ID, "login_button") #输入用户名
def type_username(self,username):
self.find_element(*self.username_loc).clear()
self.find_element(*self.username_loc).send_keys(username) #输入密码
def type_password(self,password):
self.find_element(*self.password_loc).send_keys(password) #点击登录
def type_login(self):
self.find_element(*self.login_loc).click() #创建test_user_login()函数
def test_user_login(driver, username, password):
"""测试用户名/密码是否可以登录"""
login_page = LoginPage(driver)
login_page.open()
login_page.type_username(username)
login_page.type_password(password)
login_page.type_login() #创建main()函数
def main():
driver = webdriver.Chrome()
username = '' #qq号码
password = '' #qq密码
test_user_login(driver, username, password)
sleep(3) driver.quit() if __name__ == '__main__':
main()
使用自己的账号密码登录,我把代码中的账号密码删掉了。
1.2.1 创建page类
#创建基础类
class BasePage(object):
#初始化
def __init__(self, driver):
self.base_url = 'https://mail.qq.com/'
self.driver = driver
self.timeout = 30 #定义打开登录页面方法
def _open(self):
url = self.base_url
self.driver.get(url)
self.driver.switch_to.frame('login_frame') #切换到登录窗口的iframe #定义定义open方法,调用_open()进行打开
def open(self):
self._open() #定位方法封装
def find_element(self,*loc):
return self.driver.find_element(*loc)
首先创建一个基础类BasePage,在初始化方法__init__()中定义驱动(driver),基本的URL(base_url)和超时时间(timeout)等。
定义open()方法用于打开URL网站,但它本身并未做这件事情,而是交由_open()方法来实现,而find_element()方法用于元素的定位。
1.2.2 创建LoginPage类
Page类中定义的这些方法都是页面操作的基本方法。下面根据登录页的特点再创建LoginPage类并继承Page类,这也是Page Object设计模式中最重要的对象层。
#创建LoginPage类
class LoginPage(BasePage):
username_loc = (By.ID, "u")
password_loc = (By.ID, "p")
login_loc = (By.ID, "login_button") #输入用户名
def type_username(self,username):
self.find_element(*self.username_loc).clear()
self.find_element(*self.username_loc).send_keys(username) #输入密码
def type_password(self,password):
self.find_element(*self.password_loc).send_keys(password) #点击登录
def type_login(self):
self.find_element(*self.login_loc).click()
LoginPage类中主要对登录页面上的元素进行封装,使其成为更具体的操作方法。例如,用户名、密码和登录按钮都被封装成了方法。
1.2.3 创建test_user_login()函数
#创建test_user_login()函数
def test_user_login(driver, username, password):
"""测试用户名/密码是否可以登录"""
login_page = LoginPage(driver)
login_page.open()
login_page.type_username(username)
login_page.type_password(password)
login_page.type_login()
test_user_login()函数将单个的元素操作组成一个完整的动作,而这个动作包含了打开浏览器,输入用户名/密码、点击登录等单步操作。在使用该函数时需要将driver、username、password等信息作为函数的实参,这样该函数具有很强的可重用性。
1.2.4 创建main()函数
#创建main()函数
def main():
driver = webdriver.Chrome()
username = '' #qq号码
password = '' #qq密码
test_user_login(driver, username, password)
sleep(3) driver.quit() if __name__ == '__main__':
main()
main()函数更接近于用户的操作行为。对用户来说,要进行邮箱的登录,需要关心的就是通过哪个浏览器打开邮箱网址、登录的用户名和密码是什么,至于输入框、按钮是如何定位的,则不需要关心。
这样分层的好处是,不同的层关心不同的问题。页面对象层值关心元素的定位问题,测试用例只关心测试的数据。
一个有分歧的地方是page对象是否应自身包含断言,或者仅仅提供数据给测试脚本设置断言。在page对象中包含断言的倡导者认为,这有助于避免在测试脚本中出现重复的断言,可以更容易的提供更好的错误信息,并且提供更接近制作不问风格的API。不再page对象中包含断言的倡导者则认为,包含断言会混合访问页面数据和实现断言逻辑的责任,并且导致page对象过于臃肿。
我比较赞成在page对象中不包含断言,虽然完美可以通过为常用的断言提供断言库的方式来消除重复,提供更好的诊断,但从用户的角度去自动化的观点来看,判断是否登录成功是用户需要做的事情,不应该交由页面对象层来完成。
使用Page Object模式之后的另外一个好处就是有助于降低冗余。如果需要在10个用例中输入不同的用户名/密码登录,那么用main()方法写将变得非常简洁。
因此,Page Object模型的作用在一个测试人员自己写主场景测试案例时不容易体会到的,因为你不需要和开发、业务交流案例,也不会写很多重复的动作。但是,当你真正开始测试ATDD或BDD,当你开始写一些重要的异常分支流程时,当你开始为新需求频繁维护修改案例时,就会意识到Page Object的作用。
最后,Page Object不是万灵药,也不是唯一方案,提高测试案例的可读性,避免案例步骤冗余才是终极目标。
Selenium(十九):unittest单元测试框架(五) Page Object设计模式的更多相关文章
- Selenium(十七):unittest单元测试框架(三) 脚本分析、编写Web用例
1. 带unittest的脚本分析 也许你现在心里还有疑问,unittest框架与我们前面所编写的Web自动化测试之间有什么必然联系吗?当然有,既然unittest可以组织.运行测试用例,那么为什么不 ...
- Selenium(十五):unittest单元测试框架(一) 初识unittest
1. 认识unittest 什么是单元测试?单元测试负责对最小的软件设计单元(模块)进行验证,它使用软件设计文档中对模块的描述作为指南,对重要的程序分支进行测试以发现模块中的错误.在python语言下 ...
- Selenium(十八):unittest单元测试框架(四) HTML测试报告
1. HTML测试报告 对测试人员来而言,测试的产出很难衡量.换句话说,测试人员的价值比较难以量化和评估,相信这一点对软件测试人员来说深有体会.我们花费了很多时间与精力所做的自动化测试也是如此.所以, ...
- Selenium(十六):unittest单元测试框架(二) 初识unittest(续)
1. 认识unittest(续) 关于unittest单元测试框架,还有一些问题值得进一步探讨.你可能在前一章的学习过程中产生了一些疑问,也许你会在本节中找到答案. 1.1 用例执行的顺序 用例的执行 ...
- 华为五年自动化测试工程详细解说:unittest单元测试框架
一.单元测试框架说明 单元测试是指在编程中,针对程序模块的最小单元(类中的方法)进行正确性检验的测试工作.python+selenium自动化测试中通常使用unittest或者pytest作为单元 ...
- Selenium+Python ---- 免登录、等待、unittest单元测试框架、PO模型
1.免登录在进行测试的过程中难免会遇到登录的情况,给测试工作添加了工作量,本文仅提供一些思路供参考解决方式:手动请求中添加cookies.火狐的profile文件记录信息实现.人工介入.万能验证码.去 ...
- Python+selenium之简单介绍unittest单元测试框架
Python+selenium之简单介绍unittest单元测试框架 一.unittest简单介绍 unittest支持测试自动化,共享测试用例中的初始化和关闭退出代码,在unittest中最小单元是 ...
- Python+Selenium框架设计篇之-简单介绍unittest单元测试框架
前面文章已经简单介绍了一些关于自动化测试框架的介绍,知道了什么是自动化测试框架,主要有哪些特点,基本组成部分等.在继续介绍框架设计之前,我们先来学习一个工具,叫unittest. unit ...
- Python+Selenium ----unittest单元测试框架
unittest是一个单元测试框架,是Python编程的单元测试框架.有时候,也做叫做“PyUnit”,是Junit的Python语言版本.这里了解下,Junit是Java语言的单元测试框架,Java ...
随机推荐
- AQS系列(六)- Semaphore的使用及原理
前言 Semaphore也是JUC包中一个用于并发控制的工具类,举个常用场景的例子:有三台电脑五个人,每个人都要用电脑注册一个自己的账户,这时最开始只能同时有三个人操作电脑注册账户,这三个人中有人操作 ...
- Typroa 常用快捷键
Typora 常用快捷键 文件操作 Ctrl + N :新建文件 Ctrl + shift + N :新建窗口 Ctrl + O :打开 Ctrl + P : 快速打开(快速打开之前编辑过的历史文件) ...
- [译]C# 7系列,Part 7: ref Returns ref返回结果
原文:https://blogs.msdn.microsoft.com/mazhou/2017/12/12/c-7-series-part-7-ref-returns/ 背景 有两种方法可以将一个值传 ...
- 管道及 I/O 重定向
I/O重定向 I/O Redirection 标准输入.标准输出.标准错误输出重定向及综合案例输入重定向及结合案例 标准输入.标准输出.标准错误 file descriptors (FD,文件描述符 ...
- PHP中使用date获取上月最后一天出现的问题
上次做项目时,发现一个问题,这里记录一下: 问题: 在使用date函数获取上一个月最后一天或下个月最后一天时,如果当前日期是31号,获取的数据有问题. // 2019-12-01 正确应该是 2019 ...
- Docker安全扫描工具之docker-bench-security
简介 Docker Bench for Security检查关于在生产环境中部署Docker容器的几十个常见最佳实践.这些测试都是自动化的,其灵感来自CIS Docker基准1.2.0版. 这种安全扫 ...
- Azure 上通过Automation 实现定时开关虚拟机
更多内容,请关注公众号: Azure Automation 可以提供一些自动化的功能,比如我们可以指定在每天早上6点开启虚拟机,每天晚上8点关闭虚拟机.同时还提供一些基于监控参数的自动化配置.今天的主 ...
- VM虚拟机与本地网络互通配置
一.设置虚拟机网络 1. 查看虚拟机网络NAT设置(VMnet8) 2. 设置虚拟机网络适配器为NAT模式 三.设置本机VMnet8网络属性 三.设置Linux网络属性 1. 查看 ip addr 2 ...
- Spring Cloud系列:不重启eureka,清除down掉的服务
场景描述 做项目的时候,我的服务改了个ip,然后重新启动后,原ip的服务down掉了,但是没有清楚掉,还在上面,导致我用swagger测试的时候,访问不到真正up的程序.重启eureka又不划算,于是 ...
- LeetCode刷题191218
好多天没有更新了,今天有空,刷一道. 算法第5题 给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为 1000. 示例 1: 输入: "babad"输出: ...