使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 20--PO(POM) 设计模式和用例撰写
测试学习记录,仅供参考!
WEB自动化之掌握测试用例的撰写
常规(正常的写法):以设计功能测试的测试用例思路(如自顶向下等)来输写;
1、以开源电商商城系统测试项目 http://localhost:8088/ecshop/user.php 网站中的登录页面为例;
若是测试登录,需先定位到元素,输入内容(用户名、密码),点击立即登陆按钮,然后再断言结果;
注册测试登录账号;
2、在项目根目录 testcase 软件包下新建名称为 login 的目录文件,在 login 目录下新建名称为 test_login.py 的 Python 文件;
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleepclass TestLogin:# 登录成功def test_login_success(self):driver = webdriver.Edge()# 打开浏览器网址driver.get('http://localhost:8088/ecshop/user.php')# 定位到登录页面的元素username = driver.find_element(By.NAME, 'username')password = driver.find_element(By.NAME, 'password')submit = driver.find_element(By.NAME, 'submit')# 输入用户名、密码,点击登录按钮username.send_keys('admin123')password.send_keys('123456')submit.click()# 断言结果success_ele = driver.find_element(By.XPATH, '//*[@id="ECS_MEMBERZONE"]/font/font')assert success_ele != ''sleep(2)# 登录失败def test_login_failed(self):pass
断言对象可以是页面标题、URL、登录成功的用户名称、登录成功返回信息等等;
继续编写登录失败场景的测试用例;
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleepclass TestLogin:# 登录成功def test_login_success(self):driver = webdriver.Edge()# 打开浏览器网址driver.get('http://localhost:8088/ecshop/user.php')# 定位到登录页面的元素username = driver.find_element(By.NAME, 'username')password = driver.find_element(By.NAME, 'password')submit = driver.find_element(By.NAME, 'submit')# 输入用户名、密码,点击登录按钮username.send_keys('admin123')password.send_keys('123456')submit.click()# 断言结果success_ele = driver.find_element(By.XPATH, '//*[@id="ECS_MEMBERZONE"]/font/font')assert success_ele != ''sleep(2)# 登录失败def test_login_failed(self):driver = webdriver.Edge()# 打开浏览器网址driver.get('http://localhost:8088/ecshop/user.php')# 定位到登录页面的元素username = driver.find_element(By.NAME, 'username')password = driver.find_element(By.NAME, 'password')submit = driver.find_element(By.NAME, 'submit')# 输入用户名、密码,点击登录按钮username.send_keys('admin123')password.send_keys('123456789')submit.click()# 断言结果assert '系统提示' in driver.titlesleep(2)
3、可以看到当前的测试用例每一个都得去写 一个初始化浏览器对象、定位元素、输入、断言,结合之前学习到的内容,做一些优化;把浏览器初始化对象放到前后置操作里面;
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleepclass TestLogin:# 前置操作def setup(self):# 前面加上 self. 把它变成全局的,就可以在其他测试用例中调用这个对象了self.driver = webdriver.Edge()# 登录成功def test_login_success(self):# 打开浏览器网址self.driver.get('http://localhost:8088/ecshop/user.php')# 定位到登录页面的元素username = self.driver.find_element(By.NAME, 'username')password = self.driver.find_element(By.NAME, 'password')submit = self.driver.find_element(By.NAME, 'submit')# 输入用户名、密码,点击登录按钮username.send_keys('admin123')password.send_keys('123456')submit.click()# 断言结果success_ele = self.driver.find_element(By.XPATH, '//*[@id="ECS_MEMBERZONE"]/font/font')assert success_ele != ''sleep(2)# 登录失败def test_login_failed(self):# 打开浏览器网址self.driver.get('http://localhost:8088/ecshop/user.php')# 定位到登录页面的元素username = self.driver.find_element(By.NAME, 'username')password = self.driver.find_element(By.NAME, 'password')submit = self.driver.find_element(By.NAME, 'submit')# 输入用户名、密码,点击登录按钮username.send_keys('admin123')password.send_keys('123456789')submit.click()# 断言结果assert '系统提示' in self.driver.titlesleep(2)# 后置操作def teardown(self):# 关闭浏览器self.driver.quit()
温馨提醒:
WARNING selenium.webdriver.common.selenium_manager:selenium_manager.py:127 Exception managing MicrosoftEdge: error sending request for url (https://msedgedriver.azureedge.net/LATEST_RELEASE_138_WINDOWS)
警告selenium.webdriver.comy.selenium_manager:selenium_manager.py:127管理MicrosoftEdge时发生异常:发送url请求时出错
pytest.PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.
testcase/login/test_login.py::TestLogin::test_login_failed is using nose-specific method: `setup(self)`
To remove this warning, rename it to `setup_method(self)`
See docs: https://docs.pytest.org/en/stable/deprecations.html#support-for-tests-written-for-nose
\.venv\lib\site-packages\_pytest\python.py:887: PytestRemovedIn8Warning
控制台警告信息(网络查找问题原因)
表明在代码中使用了将在 Pytest 8.0 版本中移除的特性或写法。应该检查测试代码中使用了哪些即将被废弃的特性,并更新使用推荐的新特性。
自7.2版本起已弃用。在8.0版本中删除。现在已弃用对运行为nose编写的测试的支持。
nose只处于维护模式多年,维护插件并非易事,因为它会溢出代码库
解决方法:降低 pytest 版本,指定安装符合要求的(如需使用最新版本的,自行更改测试代码)
检查浏览器驱动版本(没有大版本更新,浏览器驱动问题基本可以排除)
4、 添加日志信息;
在 testcase 软件包下新建名称为 conftest.py 配置文件;与 @pytest.fixture 装饰器结合的全局前后置应用;
import pytest
from util_tools.logs_util.recordlog import logs@pytest.fixture(autouse=True)
def log_outputs():logs.info('------测试用例开始执行------')yieldlogs.info('------测试用例执行完毕------')
5、总结测试用例编写
每一个测试用例都这么写的话,后续维护起来会比较麻烦,这样写自动化测试的成本就远远大于做功能测试手动测试的成本了;若某一天,开发人员更改了某一个页面,得去测试用例里面去找相应的改动点修改;
这是最初没有采取任何措施任何设计模式的场景,后面引入 PO(POM) 模式,使用这种模式之后,可以把一些元素定位、操作等放到一个文件里面去,测试用例只需继承就可以去实现一系列相关操作的逻辑调用;这样后续维护起来会比较方便;
PO(POM)设计模式
在 web 自动化、app 自动化测试中一种最常用的设计模式,亦是用的比较多的;使用这种设计模块用于管理页面和测试用例方法;概念是比较抽象的,需要自行理解;
概念
Page Object Model(POM)是一种设计模式(页面对象模式),是为 Web UI 自动化测试创建对象库;通常在 UI 自动化测试中使用,特别是在 Selenium 自动化测试中,此种模式的目的是将页面的功能(页面元素和页面操作)和测试脚本分离,以提高自动化测试代码的可维护性和可重用性;
Page Object 模式将每个页面抽象为一个类,或者说是对每个页面进行抽象或建模的过程,是把一个具体的页面转化为编程语言当中的一个对象,页面特性转化成对象属性,页面操作转化为对象方法,该类包含页面的元素和操作;测试脚本通过调用 Page Object 类的方法来执行测试,在这种模式下,应用涉及的每一个页面应该定义为一个单独的类,类中应该包含此页面上的页面元素对象和处理这些元素对象所需要的方法等,将流程所关联的页面作为对象,将对象串联起来,形成一个个不同的流程;目前 PO(POM)设计模式仍是业内公认最佳的设计模式。
POM模式的主要概念:
页面类(Page Class):
每个被测试的页面都有一个相应的 Page class。这个类维护页面的所有元素和操作。元素通常被定义为类的变量,而操作通常被定义为类的方法。
行为和状态分离:
Page Object 模式鼓励将页面的行为(例如点击按钮、填写表单)和状态(例如页面标题、元素可见性)封装到页面类中。这样,测试脚本可以专注于测试逻辑,而不必关心页面的具体实现。
可重用性:
由于每个页面都有一个对应的 Page 类,这些类可以在不同的测试用例中被重复使用。如果页面发生变化,只需要更新相关的 Page 类,而不是每个测试用例。
易读性:
通过将页面的结构和行为分离,测试脚本的可读性得到提高。测试脚本更加简洁,易于理解,因为不需要包含大量的页面操作和元素查找代码。
未完待续。。。