在Pytest框架实战一中详细地介绍了Pytest测试框架在参数化以及Fixture函数在API测试领域的实战案例以及具体的应用。本文章接着上个文章的内容继续阐述Pytest测试框架优秀的特性以及在自动化测试领域的实战。
conftest.py
在上一篇文章中阐述到Fixture函数的特性,第一是函数的返回值,第二是测试固件的特性。但是在实际的工作里面会有非常多的Fixture函数,那么怎么合理的安排更加友好呢?建议把公共的部分剥离出来统一的管理和调用,而非公共的部分就使用常规的函数来处理就可以了。在API自动化测试中公共的部分主要是认证授权的机制,也就是登录成功后返回的TOKEN在后续的请求中需要带上这个TOKEN才能够获取服务端的认可客户端才能够获取资源信息。在Pytest测试框架中可以把Fixture函数分离到conftest.py的文件中,这个文件它的特性具体如下。
-
它是一个模块,但是这个模块不需要导入就可以使用里面的Fixture函数
-
在目录结构的设计上,conftest.py文件建议在项目的第一层级目录结构中
-
conftest.py就是用来分离公共的Fixture函数
结合第一篇文章的案例,下来把登录认证授权代码分离到conftest.py文件中,这样测试模块使用的时候直接调用它就可以了,分离到conftest.py模块里面的代码如下。
#!/usr/bin/python3
#coding:utf-8
# author:wuya
import pytest
import requests
import json
@pytest.fixture()
def access_token():
r=requests.post(
url='http://47.95.142.233:8000/login/auth/',
json={"username":"13484545195","password":"asd888"})
return r.json().get('token',None)
@pytest.fixture()
def headers(access_token):
return {'Authorization':'JWT {token}'.format(token=access_token)}
如上是把登录认证授权的代码分离到conftest.py的文件中,下来在测试模块里面直接调用就可以了,涉及到的测试模块的代码如下:
#! /usr/bin/env python
# -*- coding:utf-8 -*-
# author:wuya
import pytest
import requests
import json
def writeID(prodctID):
json.dump(str(prodctID),open('prodctID','w'))
def getID():
return int(json.load(open('prodctID')))
def addProduct(headers):
r=requests.post(
url='http://47.95.142.233:8000/interface/product/',
json={"name":"测试数据","product_type":"WEB","version":"1.0","master":"无涯","description":"测试"},
headers=headers)
writeID(prodctID=r.json()['id'])
return r
def delProduct(headers):
r=requests.delete(
url='http://47.95.142.233:8000/interface/product/{productID}/'.format(productID=getID()),
headers=headers)
return r
@pytest.fixture()
def init(headers):
addProduct(headers)
yield
delProduct(headers)
def test_so_product(init,headers):
'''产品搜索验证'''
r=requests.get(
url='http://47.95.142.233:8000/interface/products?name=测试数据',
headers=headers)
如上可以看到init()函数也是Fixture函数,但是并没有分离到conftest.py文件中,理由主要是init()函数它是基于业务需求而编写的Fixture函数但是并不是公共的Fixture函数,在后面随着编写测试用例越来越多的情况下,这种基于业务的Fixture函数会特别多。如上的代码执行后的结果信息如下图所示。
在企业级的API自动化测试框架中,完全的可以把conftest.py应用进去,特别是登录认证授权的这部分特性功能。
命令行解释器
在微服务架构的产品中存在一个特性就是使用不同的租户来登录系统,访问的业务形态都是一样的,但是在底层会针对数据做隔离。正因为如此,所以就会有众多的集群模式来满足底层的计算能力。那么这样在编写的测试用例上,就需要更多的灵活性,这个灵活性主要指的是不能把登录的账户和密码写死,要满足不同的集群验证模式,那么也就意味着登录的用户是可以自定义的,但是这个自定义的用户必须是系统已被订阅的租户。这样设计的优势就是可以满足不同集群验证产品的特性以及定时轮训检测不同集群的底层计算能力和系统的稳定性。使用这部分可以使用Pytest测试框架的命令行解释权完全能够解决这部分,下面针对之前的代码进行改造,把登录系统的账户与密码完全的自定义化,改造后的代码如下。
#!/usr/bin/python3
#coding:utf-8
# author:wuya
import pytest
import requests
import json
def pytest_addoption(parser):
'''添加pytest的自定义命令行参数'''
parser.addoption(
'--username',action='store',default='13484545195',help='myoption: type1 or pyte2'
)
parser.addoption(
'--password',action='store',default='asd888',help='myoption: type1 or pyte2'
)
@pytest.fixture()
def username(request):
return request.config.getoption('--username')
@pytest.fixture()
def password(request):
return request.config.getoption('--password')
@pytest.fixture()
def access_token(username,password):
r=requests.post(
url='http://47.95.142.233:8000/login/auth/',
json={"username":username,"password":password})
return r.json().get('token',None)
@pytest.fixture()
def headers(access_token):
return {'Authorization':'JWT {token}'.format(token=access_token)}
在如上的代码中,完全的把登录的账户与密码分离了出来,这样就可以满足上面说的针对不同的集群以及集群规模化的验证模式,而且在上面也使用了默认的模式,也就是说执行的时候可以指定自定义的账户与密码,如下执行的方式是指定了账户与密码(自定义但是必须是指定的账户密码是系统的用户)
pytest -v -s test_login.py --username=13588545195 --password=asd888
执行后的结果信息如下所示。
也可以执行的时候使用默认命令行指定的账户与密码,执行以及执行结果如图所示。
如上所示,使用Pytest的命令行解释器的特性,可以很轻松的解决了执行过程中随意的制定自定义的变量。
pytest.ini
在Pytest框架中,使用conftest.py是为了更好地分离Fixture函数,这样能够达到Fixture函数的共享机制,从而能够在各个模块以及测试函数里面调用,从而减少重复代码的数量。一般而言编写的测试类都是以Test开头,编写的测试函数都是以test开头,而所有的测试模块在test的包下,以及执行的过程中为了输出详细的信息以及打印输出的信息,在执行的时候都会带上-v与-s的命令,当然还有其他的信息。这些都是完全可以分离到pytest.ini的文件中,由它进行统一的管理。pytest.ini地工程的第一层级的位置,如下内容是pytest.ini文件内容。
[pytest]
#指定执⾏时候的默认信息
addopts= -v -s --driver Chrome --html=report.html --reruns 3
#注册标记
markers=
login:执⾏标记为login的测试函数
register:执⾏标记为register的测试函数
#指定Pytest的最低版本号
minversion=3.0
#指定忽略执⾏⽬录
norecursedirs=.pytest_cache
#指定测试⽬录
testpaths=test
#指定测试模块搜索的规则
python_files=test_*
#指定测试类搜索的规则
python_classes=Test*
#指定测试函数搜索规则
python_functions=test_*
如上种指定了测试目录以及测试框架执行过程中测试的搜索规则。使用了pytest.ini的优势是后续执行过程中就不需要带上如-s这样的命令以及指定执行的目录。
tox.ini
tox.ini与pytest.ini是一样的,不过使用tox.ini可以代替pytest.ini的配置文件,而且在tox.ini的配置文件里面可以指定多个不同解释器的版本,这样的优势是在执行具体的测试用例的过程中就能够兼顾到多个不同Python解释器版本执行的情况。tox是一个命令行的工具,它允许测试在多种环境下执⾏,它的⼯作流程具体可以理解为:通过setup.py⽂件为待测程序创建源码安装包,这样就会查看tox.ini中所有的环境设置。把所有pytest.ini的配置都移动到 tox.ini⾥⾯,tox.ini文件内容如下。
[tox]
envlist = py3.5, py3.6, py3.7, py3.8, py3.9, py3.10, py3.11
skipsdist = True
indexserver =
default = https://pypi.doubanio.com/simple
[testenv]
install_command = pip install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host irrors.aliyun.com {opts} {packages}
deps =
-rrequirements.txt
commands = coverage erase
py.test --cov={toxinidir} -sx test
coverage html
setenv =
PYTHONPATH = {toxinidir}/py3
[testenv:dev]
deps = pytest
commands = {posargs:py.test}
[pytest]
#指定执⾏时候的默认信息
addopts= -v -s
#注册标记
markers=
login:执⾏标记为login的测试函数
register:执⾏标记为register的测试函数
#指定Pytest的最低版本号
minversion=3.0
#指定忽略执⾏的⽬录
norecursedirs=.pytest_cache
#指定测试⽬录
testpaths=test
#指定测试模块搜索的规则
python_files=test_demo.py
#指定测试类搜索的规则
python_classes=Test*
#指定测试函数搜索规则
python_functions=test_*
如上所示,指定了多个不同Python解释器的版本。执行的命令直接就是tox,执行后就会执行指定目录下所有的测试用例并且会在指定的Python解释器版本中都会执行,执行结果如下图。
在不管是UI的测试框架还是API的测试框架中,把Pytest测试框架的配置引入进去,这样执行的时候就不需要刻意的指定被执行的目录以及各种命令,更多的精力可以聚焦于测试场景的编写以及代码的封装。