伪原创网站源码( 如何面对UI变化时,测试用例也要跟着变化?(组图))
优采云 发布时间: 2022-03-15 21:01伪原创网站源码(
如何面对UI变化时,测试用例也要跟着变化?(组图))
PageObjects 简介
在为 UI 页面(如网页、移动页面)编写测试用例时,测试用例中有很多元素和操作细节。UI变化时如何面对测试用例变化的问题?PageObject 设计模式首次亮相(由 IT 大亨 Martin Flower 提出)。
在使用 UI 自动化测试工具(Selenium、Appium 等)时,如果没有统一的规范模式,随着用例数量的增加会变得难以维护,但 PageObject 保持自动化脚本有序,单独维护页面和封装了细节,并且可以使测试用例更加健壮而无需进行重大更改。
页面对象使用
具体方法:将元素信息和操作细节封装到Page类中,在测试用例上调用Page对象(PageObject)。例如,如果有一个函数“选择专辑标题”,则需要为其创建一个函数 selectAblumWithTitle()。操作细节在函数内部。findElementsWithClass('album') 等:
以选择“获取专辑标题”为例,伪代码如下:
selectAblumWithTitle() {
#选取相册
findElementsWithClass('album')
#选取相册标题
findElementsWithClass('title-field')
#返回标题内容
return getText()
}
PageObject的主要原理是提供一个简单的接口(或函数,如上面的selectAblumWithTitle),让调用者可以对页面做任何操作,点击页面元素,在输入框中输入内容等。因此,如果您想访问文本字段,页面对象应该具有获取和返回字符串的方法。页面对象应该封装对数据的操作细节,例如查找元素和点击元素。当一个页面元素改变时,你应该只改变Page类中的内容,而不是调用它的地方。
不要为每个 UI 页面创建页面类,而应该只为页面中重要的元素创建页面类。例如,如果一个页面显示了多个专辑,则应该创建一个专辑列表页面对象,其中收录许多专辑页面对象。如果某些复杂的 UI 层次结构仅用于组织 UI,那么它不应该出现在页面对象中。页面对象的目的是通过对页面进行建模来使应用程序的用户有意义:
如果要导航到另一个页面,初始页面对象应该返回另一个页面对象,例如点击注册进入注册页面,你应该在代码中返回Register()。如果要获取页面信息,可以返回基本类型(字符串、日期)。
建议不要在页面对象中放置断言。应该测试页面对象,而不是让页面对象自己测试。页面对象的职责是提供有关页面状态的信息。Page Object 在这里只用 HTML 描述,这种模式也可以用来隐藏 Java swing UI 细节,它可以与所有 UI 框架一起使用。
PageObject的六大原则
Selenium 为 PageObject 的核心思想浓缩了六大原则。只有掌握了六大原则的精髓,才能进行PageObject的最佳实践练习:
public 方法表示页面提供的服务。不要暴露页面详细信息。不要混合断言和操作细节。该方法可以返回新打开的页面。不要将整个页面内容放在 PO 中。同样的行为会产生不同的结果,可以封装不同的结果
下面,对以上六项原则进行更详细的实战解释:
原则一:将功能(或服务)封装在页面中,例如点击页面中的一个元素,可以进入一个新页面,那么可以为此服务封装“进入新页面”方法。原则二:封装细节,只对外提供方法名(或接口)。原则 3:不要在封装的操作细节中使用断言,将断言放在单独的模块中,例如测试用例。原理四:点击一个按钮会打开一个新页面,可以使用return方法表示跳转,例如return MainPage()表示跳转到新的PO:MainPage。原则五:只为页面中的重要元素设计PO,不重要的内容丢弃。原则 6:一个动作可能产生不同的结果。例如,点击一个按钮后,点击可能会成功,也可能会失败。两个结果分别封装了两个方法,click_success和click_error。基于企业微信的PO实际案例
以企业微信首页为例。企业微信首页主要有即时注册和企业登录两大功能。
企业微信网址:
点击企业登录,进入登录页面,扫码登录并注册为企业。
点击企业注册,进入注册页面,输入相关信息进行注册。
使用Page Object原理对页面进行建模,涉及到三个页面:首页、登录、注册。在代码中创建对应的三个类Inde、Login、Register:
不管登录页面有多少元素,隐藏内部界面控件,登录成功和失败会分别返回不同的页面。通过方法返回值判断登录是否符合预期的UML图。
实际代码
目录结构
BasePage 是所有页面对象的父类。它为子类提供公共方法。例如,下面的 BasePage 提供了初始化驱动和退出驱动。代码中,在base_page模块的BasePage类中使用了__init__initial方法来初始化操作,包括驱动的多路复用、驱动的赋值、全局等待(隐式等待)的设置等:
from time import sleep
from selenium import webdriver
from selenium.webdriver.remote.webdriver import WebDriver
class BasePage:
def __init__(self, driver: WebDriver = None):
#此处对driver进行复用,如果不存在driver,就构造一个新的
if driver is None:
# Index页面需要用,首次使用时构造新driver
self._driver = webdriver.Chrome()
# 设置隐式等待时间
self._driver.implicitly_wait(3)
# 访问网页
self._driver.get(self._base_url)
else:
# Login与Register等页面需要用这个方法,避免重复构造driver
self._driver = driver
def close(self):
sleep(20)
self._driver.quit()
Index是企业微信首页的页面对象。它有两种方法,一种是进入注册页面对象,另一种是进入登录页面对象。这里return方法返回page对象实现页面跳转,例如:goto_register方法返回Register实现从首页到Registration页面的跳转:
from selenium.webdriver.common.by import By
from test_selenium.page.base_page import BasePage
from test_selenium.page.login import Login
from test_selenium.page.register import Register
class Index(BasePage):
_base_url = "https://work.weixin.qq.com/"
# 进入注册页面
def goto_register(self):
self._driver.find_element(By.LINK_TEXT, "立即注册").click()
# 创建Register实例后,可调用Register中的方法
return Register(self._driver)
# 进入登录页面
def goto_login(self):
self._driver.find_element(By.LINK_TEXT, "企业登录").click()
# 创建Login实例后,可调用Login中的方法
return Login(self._driver)
login是登录页面的页面对象。主要功能有:进入注册页面,扫描二维码。因此,创建了两个方法来表示两个函数:scan_qrcode 和 goto_registry。代码与上面类似,但更多介绍:
from selenium.webdriver.common.by import By
from test_selenium.page.base_page import BasePage
from test_selenium.page.register import Register
class Login(BasePage):
# 扫描二维码
def scan_qrcode(self):
pass
# 进入注册页面
def goto_registry(self):
self._driver.find_element(By.LINK_TEXT, "企业注册").click()
return Register(self._driver)
Register是注册页面的page对象。主要功能是填写正确的注册信息。填写错误信息时,返回错误信息。register 方法实现了正确的表单填写并在完成时返回自身(页面仍然停留在注册页面上)。get_error_message 方法实现错误填充。如果填写错误,则采集错误内容并返回:
from selenium.webdriver.common.by import By
from test_selenium.page.base_page import BasePage
class Register(BasePage):
# 填写注册信息,此处只填写了部分信息,并没有填写完全
def register(self, corpname):
# 进行表格填写
self._driver.find_element(By.ID, "corp_name").send_keys(corpname)
self._driver.find_element(By.ID, "submit_btn").click()
# 填写完毕,停留在注册页,可继续调用Register内的方法
return self
#填写错误时,返回错误信息
def get_error_message(self):
# 收集错误信息并返回
result=[]
for element in self._driver.find_elements(By.CSS_SELECTOR, ".js_error_msg"):
result.append(element.text)
return result
test_index 模块是对上述功能的测试。它独立于页面类。在TestIndex类中,只需要调用页面类提供的方法即可。例如下面注册页面和登录页面的测试使用了test_register和test_login方法:
from test_selenium.page.index import Index
class TestIndex:
# 所有步骤前的初始化
def setup(self):
self.index = Index()
# 对注册功能的测试
def test_register(self):
# 进入index,然后进入注册页填写信息
self.index.goto_register().register("霍格沃兹测试学院")
# 对login功能的测试
def test_login(self):
# 从首页进入到注册页
register_page = self.index.goto_login().goto_registry()\
.register("测吧(北京)科技*敏*感*词*")
# 对填写结果进行断言,是否填写成功或者填写失败
assert "请选择" in "|".join(register_page.get_error_message())
# 关闭driver
def teardown(self):
self.index.close()
以上,欢迎留言讨论。更高级的自动化测试实践,建议学习霍格沃茨测试学院出品的《高级测试开发实践》课程。
单击此处获取更多信息和视频
:///t/topic/3595