1.PO设计模式
PO模式是指将页面元素的定位以及元素的操作分离出来,测试用例脚本直接调用这些封装好的元素操作来组织测试用例,从而实现了测试用例脚本和元素定位、操作的分离。
优点:
● 抽象出页面对象可以在很大程度上降低开发人员修改页面代码对测试的影响。
● 可以在多个测试用例中复用一部分测试代码。
● 测试代码变得更易读、灵活、可维护。
● 测试团队可以分工协作,部分人员封装测试元素对象和操作,部分人员应用封装好的元素操作来组织测试用例。
将系统按页面分成3层——元素对象层、元素操作层、页面业务场景层:
● 元素对象层:封装定位元素的方法。
Test/PageObject/login_page.py:
# 页面元素对象层
class LoginPage(object):
def __init__(self, driver):
# 私有方法
self.driver = driver
def find_username(self):
# 查找并返回"用户名"文本框元素
# ele = self.driver.find_element_by_id('username')
# ele = self.driver.find_element_by_name('username')
ele = Base(self.driver).get_element('name,username')
print("找到用户名输入框")
return ele
def find_password(self):
# 查找并返回"密码"文本框元素
# ele = self.driver.find_element_by_id('password')
ele = Base(self.driver).get_element('name,password')
print("找到密码输入框")
return ele
def find_login_btn(self):
# 查找并返回"登录"按钮元素
# ele = self.driver.find_element_by_id('login-submit')
ele = Base(self.driver).get_element('xpath,//*[@id="app"]/div/form/button')
return ele
def find_login_name(self):
# 查找并返回登录成功后的用户名元素
# ele = self.driver.find_element_by_id('loggedas')
ele = Base(self.driver).get_element('id,loggedas')
return ele
def find_login_failed_info(self):
# 查找并返回登录失败后的提示信息元素
# ele = self.driver.find_element_by_id('flash_error')
ele = Base(self.driver).get_element('class,el-form-item__error')
return ele
def find_dashboard(self):
# 查找登录成功后的dashboard元素
ele = Base(self.driver).get_element('xpath,//*[@id="breadcrumb-container"]/span/span/span[1]/span')
return ele
● 元素操作层:借助元素对象层封装元素的操作方法。
# 页面元素操作层
class LoginOper(object):
def __init__(self, driver):
# 私有方法,调用元素定位的类
self.login_page = LoginPage(driver)
self.driver = driver
def input_username(self, username):
# 对"用户名"文本框做clear和send_keys操作
self.login_page.find_username().clear()
print("清除输入的用户名")
self.login_page.find_username().send_keys(username)
print("输入用户名:", username)
def input_password(self, password):
# 对"密码"文本框做clear和send_keys操作
self.login_page.find_password().clear()
print("清除输入的密码")
self.login_page.find_password().send_keys(password)
print("输入密码:", password)
# def click_login_btn(self):
# 对"登录"按钮做单击操作
# self.login_page.find_login_btn().click()
def click_login_btn(self):
ele = self.login_page.find_login_btn()
ActionChains(self.driver).double_click(ele).perform()
print("点击登录")
def get_login_name(self):
# 返回登录成功后的用户名元素
return self.login_page.find_login_name().text
def get_login_failed_info(self):
# 返回登录失败后的提示信息元素
return self.login_page.find_login_failed_info().text
def get_dashboard(self):
# 返回登录成功后的dashboard元素
return self.login_page.find_dashboard().text
● 页面业务场景层:借助元素操作层封装当前页面的业务场景。
# 页面业务场景层
class LoginScenario(object):
def __init__(self, driver):
# 私有方法:调用页面元素操作
self.login_oper = LoginOper(driver)
def login(self, username, password):
# 定义一个登录场景,用到了3个操作
self.login_oper.input_username(username)
sleep(3)
self.login_oper.input_password(password)
sleep(3)
# self.login_oper.input_verification_code()
self.login_oper.click_login_btn()
sleep(3)
通过调用页面业务场景或者元素操作封装好的方法来组织测试用例:
from selenium import webdriver
import pytest
# 导入本用例用到的页面对象文件
from Common.get_csv import get_csv
from Common.get_yaml import get_yaml
from Test.PageObject import login_page
host = get_yaml("../../Config/config.yaml", "websites", "host")
url = "http://" + host + "/#/login?redirect=%2Fdashboard"
data = get_csv("../../Data/test_001_login.csv") # 改写
# print(data)
# data = [('admin', 'error', '0'), ('admin', 'rootroot', '1')]
@pytest.mark.parametrize(("username", "password", "status", "error_text"), data)
class TestLogin():
def setup(self):
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(20)
# 访问"登录"页面
self.driver.get(url)
def teardown(self):
self.driver.quit()
def test_001_login(self, username, password, status, error_text):
# 登录的3个操作用业务场景方法一条语句代替
login_page.LoginScenario(self.driver).login(username, password)
if status == '0':
# 登录失败后的提示信息,通过封装的元素操作来代替
text = login_page.LoginOper(self.driver).get_login_failed_info()
assert text == error_text
elif status == '1':
# 登录成功后显示的用户名,通过封装的元素操作来代替
text = login_page.LoginOper(self.driver).get_dashboard()
assert "Dashboard" in text
# 增加一个断言
assert "Dashboard" in self.driver.page_source
else:
print('参数化的状态只能传入0或1')
if __name__ == '__main__':
# pytest.main(['-s', '-q', '--alluredir', '../report/'])
pytest.main(['-s', 'test_001_login.py'])
2.问题汇总:
(1)隐藏的元素定位
隐藏的元素通常会使用hidden属性,或通过CSS控制display=none实现,这些元素在selenium中通常是不能直接定位的,只有当它们在界面中可见时,才能被定位。
(2)selenium如何提交点击事件的成功率
- 使用显性等待,等待出现后点击
- 确定元素没有被其他元素遮挡
- 确定是否点击元素中心位置
- 点击只能在viewport中,元素不可见时,需要将元素滚动到可视范围之内
- 使用ActionChains中的click方法,或者原生JS中的click语句,代替el.click
(3)如何提高selenium测试用例执行效率
- 尽量避免使用sleep
- 对于firefox,考虑使用测试专用的profile,因为每次启动浏览器的时候,firefox会创建一个新的profile,对于这个新的profile,所有静态资源都是从服务器直接下载,而不是从缓存里面加载,这就导致网络不好的时候,用例运行速度特别慢。
- 使用更高配置的电脑和更快的网络环境
- 使用并发执行方式
- 使用selenium gird调度多台执行节点
(4)如何提高selenium测试用例运行的稳定性
- 尽量使用隐性和显性等待机制
- 注意元素定位表达式的编写,避免使用绝对路径和多节点关系定位,减少索引使用,避免动态属性的使用
- 尽量避免用例之前相互关联
(5)自动化用例执行策略
- 每日执行:比如每天晚上在主干执行一次
- 周期执行:每隔2小时在开发分支执行一次
- 动态执行:每次代码有提交就执行
(6)UI自动化需要做数据库校验吗
(7)有没有遇到元素定位不到,怎么处理
- 页面加载元素过慢,加等待时间,或者使用显性等待
- 页面有frame框架,需要先跳入frame框架才能定位
- 可能该元素是动态元素,定位方式要优化,可使用部分元素定位,或通过父节点或兄弟节点定位。
- 可能识别了元素,但是不能操作,比如元素不可用或者不可写等。需要使用JS先把前置的操作完成。
(8)UI自动化不适用的场景
(1)功能测试优先级高于UI测试,需要快速验证功能的项目
(2)UI界面频繁变化的项目
(3)跨平台测试