前言
因为要迁移 git,整理部门的项目时,看到了快有 4 年的 web 自动化项目,是个 webdriver+pytest 的。就顺便看看,还是发现不少有点意思的地方,算是个 webdriver+pytest 整合基础分享吧
项目结构
大致结构如下文章源自玩技e族-https://www.playezu.com/184591.html
+ baseTools (工具包)
----+ browserTools (浏览器工具包)
----------- browserManager.py (浏览器管理:启动、关闭、一些列封装操作)
----------- ...
----+ reportTools(报表工具包)
----+ dbTools(数据库工具包)
+ busineesPrj (业务工程)
------+ common (业务公共包)
------------+ login(页面库)
----------------------- mainLoginPage.py
----------- ...
------+ busiModule
-------------- condata.py(测试数据)
-------------- contest.py (业务包内的pytest配置)
------------+ login
----------------------- testLogin.py (具体的测试方法)
------------------------ ...
- contest.py(全局的pytest配置)
- main.py
比较简单而常见的结构,里面整合了浏览器管理、报告生成等等一些列工具及一系列处理文章源自玩技e族-https://www.playezu.com/184591.html
一些特点罗列
main.py
【1】启动时读取了一些外部参数,这个应该是和持续集成工具整合时用的
可以指定浏览器、代理、测试版本的环境及开发版本、是否冒烟、运行的业务模块
启动前还做了些残余进程的处理和临时文件的清理操作文章源自玩技e族-https://www.playezu.com/184591.html
【2】全局 contest.py 中用了不少 pytest 的特性,值得说说
全局的浏览器(webdriver)启动及关闭
@pytest.fixture(scope='session',autouse=True)
def xBM(request):
"""
session级别:整个程序运行中,只运行一次
:param request:
:return:
"""
global bm
if bm is None or bm.driver is None:
print("n----打开webdriver------")
browserType=request.config.getoption("--browserType")
IpAndPort = request.config.getoption("--proxyIpPort")
ipPortSpl = IpAndPort.split(":")
if len(ipPortSpl) != 2 or not ipPortSpl[1].isdigit():
ipPortSpl = ["",0]
chromeOptions=['--demo-type','--ignore-certificate-errors']
firefoxProfiles=[
("network.http.phishy-userpass-length", 255),
("webdriver_assume_untrusted_issuer",False),
]
bm=BrowserManager(browserType,ipPortSpl[0],int(ipPortSpl[1]),chromeOptions=chromeOptions,firefoxProfiles=firefoxProfiles)
bm.maxSizeWin()
def end():
bm.quit()
print("n----关闭webdriver------")
request.addfinalizer(end) # 最终关闭dirver
return bm
利用了 pytest.fixture 的 scope='session',这样整个自动化只会自动的启动 1 次 webdriver,并返回了封装 webdriver 的对象 xBM 这个 fixture,并在运行完成最后关闭 webdriver,形成一个闭环。
另外还用同样方式的定义了一个 redis 的客户端 fixture(不细说了)文章源自玩技e族-https://www.playezu.com/184591.html
全局的参数存储器
@pytest.fixture(scope='session',autouse=True)
def xGlobalArgs():
return {}
这个绝对是 fixture 的一个小技巧,自动化运行中的测试方法脚本之间传递参数就靠它了,比如一个测试方法获取了页面的 id,可以存在里面 xGlobalArgs["id"]="1231", 另 1 个测试方法就可以直接使用 xGlobalArgs["id"]文章源自玩技e族-https://www.playezu.com/184591.html
【3】用了一个简单的 PO 模式
先是定义了一个元素选择器类,封装 webdriver 的 browserManager 中的元素操作方法都是基于这个元素选择器类来做的(可能为了统一元素定位方式,为 UI 做准备?)文章源自玩技e族-https://www.playezu.com/184591.html
class ElementSelector():
def __init__(self,by:str,value:str):
self.by=by
self.value=value
页面元素库类,里面含元素及通用操作文章源自玩技e族-https://www.playezu.com/184591.html
from baseTools.browserTool.browerManager import BrowserManager,ElementSelector,By
class LoginPage(object):
BaseUrl='https://login.xxxx.com/'
#----元素---
EmailAddress_input=ElementSelector(By.NAME,"loginName")
Password_input=ElementSelector(By.NAME,"password")
LoginIn_button=ElementSelector(By.ID,"submit")
ErrorTips_info=ElementSelector(By.XPATH,"//ul[contains(@class,'error-tips')]")
#----方法---
def login(self,bm:BrowserManager,email:str,password:str)->bool: # 登录方法
pass
def isLogon(self,bm:BrowserManager)->bool: # 检查是否已经登录
pass
def logOut(self,bm:BrowserManager,redirectUrl=""): # 退出登录
pass
测试类直接继承了 PO 类
这样有个好处是测试方法中可以直接使用元素
@pytest.mark.parametrize 使用了数据驱动方式
@allure.feature('xxx'),@allure.description('xxx'),@allure.story('xxx'),@allure.title('xxx') 可以描述测试方法集、方法名,可以在 allure 报告中友好呈现
with allure.step("xxx") 可以定义步骤,也可以在 allure 报告中友好呈现文章源自玩技e族-https://www.playezu.com/184591.html
【4】模块下的 contest.py 也有一些 pytest 的特性操作
公共业务方法定义
@pytest.fixture(scope='function')
def xLoginAsBuyer(xBM:BrowserManager):
"""
:param xBM:
:return: 0 失败,1 成功,2 密码错误
"""
lp=LoginPage()
if lp.isLogon(xBM):
lp.logOut(xBM)
xBM.driver.get(lp.BaseUrl)
rst,err=lp.baselogin(xBM,confdata.buyer[0],confdata.buyer[1])
errMsg="[%s,%s]:%s"%(confdata.buyer[0],confdata.buyer[1],str(err)) if err else None
ignoreGuide(xBM) # 利用修改cookie及redis来清除掉各种向导、弹框
# 前置处理
yield rst==1,errMsg
# 后置处理
lp.logOut(xBM) # 退出登录
这种 fixture 需要在 test 方法的参数中,并在方法中使用才会调用(非自动的),利用 yield 分开前置处理后后置处理:
这里前置处理就是测试前登录一个公共账号返回结果,后置处理是当这个 test 结束后,自动调用退出登录方法文章源自玩技e族-https://www.playezu.com/184591.html
公共对象定义
@pytest.fixture(scope='function')
def xOraCli():
"""
oracle客户端
:return:
"""
dbClient=OracleClient('192.168.1.10',3306,'xxxx','xxxx','xxxx')
yield dbClient
dbClient.close()
这个也是同样的,这个 fixture 返回了一个 db 客户端,放在 test 方法的参数中,可以在测试方法使用这个 db 客户端进行相关操作,test 结束后自动关闭这个客户端。文章源自玩技e族-https://www.playezu.com/184591.html
[就这些]软件项目功能测试报告