当前位置:网站首页>实战 | UI 自动化测试框架设计与 PageObject 改造
实战 | UI 自动化测试框架设计与 PageObject 改造
2022-06-12 23:39:00 【华为云】
本文节选自霍格沃兹《测试开发实战进阶》课程教学内容,进阶学习文末加群。
在 UI 自动化测试过程中,面对复杂的业务场景,经常会遇到这样的挑战:
- 简单的录制/回放速度快,但无法适应复杂场景;
- 编写自动化测试脚本比较灵活,但工作量大且可维护性差;
- 以往的封装技术(PageObject)可以适应各种 UI 场景,但结构松散,无法在多项目中迁移;
因此,测试团队通常还需要一种定制测试框架,用以弥补现有框架的缺点。
测试框架封装思想
由于 UI 自动化测试框架围绕 UI 界面使用,因此,依旧选用 PageObject 设计模式对 UI 及测试进行封装,同时配合 Pytest 单元测试将脚本能够有效的组织、连贯应用起来,从而提高框架的可维护性和可读性。
由于测试框架基于 PageObject 设计模式,主要方向为 PO 改进,数据驱动,异常处理等,比如:
- 测试数据的数据驱动:将数据存储到外部 yaml 文件中,利用 yaml 工具进行数据读取;
- 数据步骤的数据驱动:将操作步骤放到外部 yaml 文件中,利用 yaml 工具对操作步骤进行读取,用专门函数解析并实现操作步骤;
- 自动化异常处理机制:对元素查找模块进行封装和改进,包括如何处理弹窗;
Page_Object 改造
作为通用的 UI 测试框架, PageObjet 不仅适用于 Web 自动化测试,也可适用 Appium 移动自动化测试,其优点如下:
- 减少代码重复
- 提高测试用例可读性
- 提高测试用例可维护性
PO 改造实例(基于雪球 App)
本案例将对雪球 App 进行 Page Objetct 封装与改进。
当启动雪球 App 时,会进入首页。点击搜索框进入搜索页,搜索某支股票然后判断股价是否大于 200:
PageObjetct 的模块关系如下,所有的模块要继承 BasePage , App 实现启动,重启,停止等操作, Main 实现进入搜索页,进入股票页等操作:
base_page 模块是所有 page 类的父类,其中定义了公共方法,比如封装下面的 find 方法后,可以让子类调用 find :
from appium.webdriver.webdriver import WebDriverclass BasePage: _driver: WebDriver def __init__(self, driver: WebDriver = None): self._driver = driver def find(self, locator, value: str = None): #如果传进来的是tuple,只需使用一个参数:locator if isinstance(locator, tuple): return self._driver.find_element(*locator) else: return self._driver.find_element(locator, value)
App 模块封装 app 的启动,重启,停止等方法,当 app 启动时会进入 main 页面,因此在下面的 main 方法要 return Main ,Main 类的定义在后面会讲解:
from appium import webdriverfrom test_appium.page.base_page import BasePagefrom test_appium.page.main import Mainclass App(BasePage): #指定app的包名和activity名 _package = "com.xueqiu.android" _activity = ".view.WelcomeActivityAlias" def start(self): #如果driver为空则初始化 if self._driver is None: caps = {} caps["platformName"] = "android" caps["deviceName"] = "hogwarts" caps["appPackage"] = self._package caps["appActivity"] = self._activity caps["noReset"] = True #初始化driver self._driver = webdriver.Remote("http://localhost:4723/wd/hub", caps) self._driver.implicitly_wait(30) #如果driver不为空,则直接启动activity else: print(self._driver) self._driver.start_activity(self._package, self._activity) return self def restart(self): pass def stop(self): pass def main(self) -> Main: #当app启动时,跳转到(实例化)Main return Main(self._driver)
Main 模块是首页的 PageObject ,其中的方法封装了首页的重要功能,比如下面代码中的 goto_search_page 封装了点击搜索并跳转到 Search 页:
from appium.webdriver.common.mobileby import MobileByfrom selenium.webdriver.common.by import Byfrom test_appium.page.base_page import BasePagefrom test_appium.page.profile import Profilefrom test_appium.page.search import Searchclass Main(BasePage): #点击搜索按钮后,进入搜索页 def goto_search_page(self): self.find(MobileBy.ID, "tv_search").click() #进入搜索页 return Search(self._driver) def goto_stocks(self): pass def goto_trade(self): pass def goto_messages(self): pass
Search 模块可以搜索一支股票,还可以获取股票的价格,比如下图:
封装代码如下:
from appium.webdriver.common.mobileby import MobileByfrom selenium.webdriver.remote.webdriver import WebDriverclass Search: _driver: WebDriver def __init__(self, driver): self._driver = driver #输入要搜索的内容 def search(self, key: str): self._driver.find_element(MobileBy.ID, "search_input_text").send_keys(key) self._driver.find_element(MobileBy.ID, "name").click() return self #获取股票价格,用于判断 def get_price(self, key: str) -> float: return float(self._driver.find_element(MobileBy.ID, "current_price").text)
最后对上述代码建立测试,新建测试模块 test_search :
import pytestfrom test_appium.page.app import Appclass TestSearch: def setup(self): self.main = App().start().main() def test_search(self): assert self.main.goto_search_page().search("alibaba").get_price("BABA") > 200
以上,供大家参考,欢迎一起留言探讨。
获取更多相关资料:请添加vx,ceshiren001
https://qrcode.ceba.ceshiren.com/link?name=article&project_id=qrcode&from=hwyun×tamp=1654998320&author=MM
边栏推荐
- 2022年危險化學品經營單比特安全管理人員考試試題及在線模擬考試
- Shardingsphere-proxy-5.0.0 deployment table implementation (I)
- Automatically obtain the position offset of member variables inside the structure
- Lua conditional statement
- The most widely used dynamic routing protocol: OSPF
- Design a MySQL table for message queue to store message data
- Record 5 - the serial port of stm32f411ceu6 realizes the sending and receiving of fixed length data and variable length data
- [SciPy optimization tutorial] v. quick solution of univariate function optimization
- Printf segment error (core dump): a problem caused by formatted output
- Analysis report on the 14th five year development plan and operation mode of China's hazardous waste treatment industry from 2022 to 2028
猜你喜欢
Leetcode 2164. 对奇偶下标分别排序(可以,一次过)
Alien Skin Exposure X7调色滤镜插件,RAW后期处理工具
How does idea switch the interface to Chinese
Examination questions and online simulation examination for safety management personnel of hazardous chemical business units in 2022
2202 - production de CV
[opencv learning] use the Tesseract OCR movement to recognize numbers
Test platform series (97) perfect the case part
Colab tutorial (super detailed version) and colab pro/colab pro+ usage evaluation
Leetcode 2200. 找出数组中的所有 K 近邻下标(可以,一次过)
Leetcode 890 finding and replacing patterns [map] the leetcode path of heroding
随机推荐
Leetcode 2200. 找出数组中的所有 K 近邻下标(可以,一次过)
M_8:设计消息队列存储消息数据的 MySQL 表格
利率降低导致债券价格上涨
OpenCV源代码编译
So, what is the difference between e.target and e.currenttarget?
The Milvus graphical management tool Attu is coming!
PHP删除二维数组中相同项的数据
The most widely used dynamic routing protocol: OSPF
Redis实现短信验证码登录
2202-簡曆制作
LeetCode 146. LRU cache
C language: how to give an alias to a global variable?
[Yugong series] wechat applet in February 2022 - Reference
Software development tools [3] theoretical basis of software development tools
細數攻防演練中十大關鍵防守點
Restrictions on MySQL function creation
Model over fitting - solution (II): dropout
Lua conditional statement
MySQL row to column, column to row, multiple columns to one row, one row to multiple columns
Summary of the lowest level error types in PHP