fixture非常有用,它们提供了固定的基准,因此测试可以被可靠地执行并产生一致的,可重复的结果。使用fixture可以设置服务,状态或其他操作环境。测试函数通过参数调用fixture方法。
fixture比setup、teardown等方法更好
1.fixture具有明确的名称,可以通过在测试功能,模块,类或整个项目中声明其使用来激活。
2.fixture以模块的方式实现,因为每个fixture方法都会触发一个fixture功能,该功能本身可以使用其他fixture。
3.fixture管理的范围从简单的单元测试到复杂的功能测试,可以根据配置和组件选项对fixture和测试进行参数化,或者在功能,类,模块或整个测试会话范围内重复使用fixture。
下面应用理解
使用测试程序
/testapi/add/
/testapi/minus/
/testapi/chengfa/
使用pytest测试类
import pytest import requests import json import os import logging class TestMath(object): @pytest.mark.parametrize('a,b',[(0.1,0.2),(1,2)]) def testadd(self,a,b): '''测试加法程序''' headers = { 'Content-Type': "application/json", } reqdata = {'a':a,'b':b} resp = requests.request(method='POST', url='http://127.0.0.1:8000/testapi/add/', verify=False, headers=headers, json=reqdata) resp = json.loads(resp.text) assert resp['status']==1 assert resp['data'] == a + b return resp['data'] @pytest.mark.parametrize('a,b',[(0.1,0.2),(1,2)]) def testminus(self,a,b): '''测试加法程序''' headers = { 'Content-Type': "application/json", } reqdata = {'a':a,'b':b} print(reqdata) resp = requests.request(method='POST', url='http://127.0.0.1:8000/testapi/minus/', verify=False, headers=headers, json=reqdata) resp = json.loads(resp.text) print(resp) assert resp['status']==1 assert resp['data'] == a - b return resp['data'] @pytest.mark.parametrize('a,b',[(0.1,0.2),(1,2)]) def testchengfa(self,a,b): '''测试加法程序''' headers = { 'Content-Type': "application/json", } reqdata = {'a':a,'b':b} print(reqdata) resp = requests.request(method='POST', url='http://127.0.0.1:8000/testapi/chengfa/', verify=False, headers=headers, json=reqdata) # warnings.warn('这是个警告', category=None, stacklevel=1, source=None) resp = json.loads(resp.text) print(resp) assert resp['status']==1 assert resp['data'] == a * b return resp['data'] if __name__ == '__main__': pytest.main(['D:/PycharmProjects/untitled/test_math.py'])
比如说要使用cookie,一般来说是需要初始化
@pytest.fixture
def mycookie(self):
self.s = requests.session()
c = requests.cookies.RequestsCookieJar()
c.set('testcookies', 'XXXXXXX')
self.s.cookies.update(c)
return self.s
测试方法中加入print(mycookie.cookies['testcookies'])
运行看见能打印XXXXXXX
conftest.py
如果在实施测试期间您意识到要使用fixture到多个测试文件中的功能,则可以将其移至一个conftest.py文件中。测试中无需在所有文件导入这个fixture,它会被pytest自动发现。夹具功能的发现始于测试类,然后是测试模块,然后是 conftest.py文件,最后是内置插件和第三方插件。
fixture作用范围
夹具是在测试首次请求时创建的,并根据以下scope的值决定fixture何时被销毁:
function:默认范围,fixture在单个测试对象运行结束时被销毁。
class:在class中的最后一个测试对象运行完被销毁。
module:在文件中的最后一个测试对象运行完被销毁。
package:在包中的最后一个测试对象运行完被销毁。
session:在整个测试阶段结束时被销毁。
import pytest # fixtures documentation order example order = [] @pytest.fixture(scope="session") def s1(): order.append("s1") @pytest.fixture(scope="module") def m1(): order.append("m1") @pytest.fixture def f1(f3): order.append("f1") @pytest.fixture def f3(): order.append("f3") @pytest.fixture(autouse=True) def a1(): order.append("a1") @pytest.fixture def f2(): order.append("f2") def test_order(f1, m1, f2, s1): assert order == ["s1", "m1", "a1", "f3", "f1", "f2"]
test_order调用的fixture将按以下顺序实例化:
s1:session是外面一层。
m1:module是第二外面一层。
a1:是一个autouse-fixture:它会在同一范围内的其它fixture之前被实例化。
f3:被同级别调用的比调用者先实例化
f1:同级别按排名顺序调用
f2:同上
使用fixture代替setup/teardown
通过使用yield
语句代替return
,yield语句之后的所有代码都将用作teardown的功能。
例如登录和退出
@pytest.fixture(scope='module',autouse=True) def logginandout(self): print('登录成功') yield print('退出成功')
test_math.py::TestMath::testadd[0.1-0.2] 登录成功
...
FAILED退出成功
改成scope='function'的话
test_math.py::TestMath::testadd[0.1-0.2] 登录成功 XXXXXXX PASSED退出成功 test_math.py::TestMath::testadd[1-2] 登录成功 XXXXXXX PASSED退出成功 test_math.py::TestMath::testminus[0.1-0.2] 登录成功 {'a': 0.1, 'b': 0.2} {'status': 1, 'message': '请求成功', 'data': -0.1} PASSED退出成功 test_math.py::TestMath::testminus[1-2] 登录成功 {'a': 1, 'b': 2} {'status': 1, 'message': '请求成功', 'data': -1} PASSED退出成功 test_math.py::TestMath::testchengfa[0.1-0.2] 登录成功 {'a': 0.1, 'b': 0.2} {'status': 1, 'message': '请求成功', 'data': 0.30000000000000004} FAILED退出成功 test_math.py::TestMath::testchengfa[1-2] 登录成功 {'a': 1, 'b': 2} {'status': 1, 'message': '请求成功', 'data': 3} FAILED退出成功
fixture参数化
可以对fixture功能进行参数设置,在这种情况下,它们将被多次调用,每次执行一组相关测试,即依赖于该fixture的测试。夹具参数化有助于为组件编写详尽的功能测试,这些组件本身可以通过多种方式进行配置。
@pytest.fixture(params= [(0.1,0.2),(1,2)],ids=['小数','整数']) def gettestdata(self,request): return request.param
加入以上fixture并修改测试方法
# @pytest.mark.parametrize('a,b',[(0.1,0.2),(1,2)]) def testadd(self,gettestdata,mycookie): '''测试加法程序''' print(mycookie.cookies['testcookies']) a,b = gettestdata[0],gettestdata[1] headers = { 'Content-Type': "application/json", } reqdata = {'a':a,'b':b} resp = mycookie.request(method='POST', url='http://127.0.0.1:8000/testapi/add/', verify=False, headers=headers, json=reqdata) resp = json.loads(resp.text) assert resp['status']==1 assert resp['data'] == a + b return resp['data']
...
pytest.main(['-v',''])运行成功
test_math.py::TestMath::testadd[小数] PASSED [ 16%]
test_math.py::TestMath::testadd[整数] PASSED [ 33%]
test_math.py::TestMath::testminus[小数] PASSED [ 50%]
test_math.py::TestMath::testminus[整数] PASSED [ 66%]
test_math.py::TestMath::testchengfa[小数] FAILED [ 83%]
test_math.py::TestMath::testchengfa[整数] FAILED [100%]
注意加入scope='class'顺序不同
test_math.py::TestMath::testadd[小数] PASSED [ 16%] test_math.py::TestMath::testminus[小数] PASSED [ 33%] test_math.py::TestMath::testchengfa[小数] FAILED [ 50%] test_math.py::TestMath::testadd[整数] PASSED [ 66%] test_math.py::TestMath::testminus[整数] PASSED [ 83%] test_math.py::TestMath::testchengfa[整数] FAILED [100%]
加入scope='function'
test_math.py::TestMath::testadd[小数] PASSED [ 16%] test_math.py::TestMath::testadd[整数] PASSED [ 33%] test_math.py::TestMath::testminus[小数] PASSED [ 50%] test_math.py::TestMath::testminus[整数] PASSED [ 66%] test_math.py::TestMath::testchengfa[小数] FAILED [ 83%] test_math.py::TestMath::testchengfa[整数] FAILED [100%]
使用usefixtures
有时测试功能不需要直接访问fixture对象。使用@pytest.mark.usefixtures直接调用
# conftest.py @pytest.fixture() def writelog(self): try: self.resultfile = open('testtext.txt', 'w+', encoding='utf-8') except Exception as e: pass if self.resultfile.writable(): self.resultfile.writelines('测试开始!') self.resultfile.writelines(' ') yield self.resultfile.writelines('测试结束!') self.resultfile.writelines(' ') self.resultfile.close()
测试类之前加上@pytest.mark.usefixtures('writelog')就能写入文件
测试开始!
测试结束!
可以指定多个灯具
可以将项目中所有测试所需的灯具放入ini文件中
# content of pytest.ini [pytest] usefixtures = writelog
使用fixture读取excel文件参数进行测试
加入Excel进行测试是比较常规的参数化做法
如
首先加入读取excel方法
def getdata(cls): file = xlrd.open_workbook('test.xlsx') sheet = file.sheet_by_index(0) rownum = file.sheet_by_index(0).nrows ablist = [] for row in range(rownum): if row: rowvalue = sheet.row_values(row) ablist.append((rowvalue[2], rowvalue[3])) return ablist #ablist = [(0.1, 0.2), (1.0, 2.0), (0.1, 2.0)]
然后用fixture调用此方法
@pytest.fixture(params= getdata('TestMath')) def gettestdata(self,request): print('参数' + str(request.param)) return request.param
查看运行结果
test_math.py::TestMath::testadd[gettestdata0] PASSED [ 11%] test_math.py::TestMath::testadd[gettestdata1] PASSED [ 22%] test_math.py::TestMath::testadd[gettestdata2] PASSED [ 33%] test_math.py::TestMath::testminus[gettestdata0] PASSED [ 44%] test_math.py::TestMath::testminus[gettestdata1] PASSED [ 55%] test_math.py::TestMath::testminus[gettestdata2] PASSED [ 66%] test_math.py::TestMath::testchengfa[gettestdata0] FAILED [ 77%] test_math.py::TestMath::testchengfa[gettestdata1] FAILED [ 88%] test_math.py::TestMath::testchengfa[gettestdata2] FAILED [100%]
标签不好看,添加一下ids
def getdata(cls): file = xlrd.open_workbook('test.xlsx') sheet = file.sheet_by_index(0) rownum = file.sheet_by_index(0).nrows title = [] ablist = [] for row in range(rownum): if row: rowvalue = sheet.row_values(row) title.append(rowvalue[1]) ablist.append((rowvalue[2], rowvalue[3])) return (title,ablist)
修改为@pytest.fixture(params= getdata('TestMath')[1],ids=getdata('TestMath')[0],在运行看运行结果
test_math.py::TestMath::testadd[两个小数] PASSED [ 11%] test_math.py::TestMath::testadd[两个整数] PASSED [ 22%] test_math.py::TestMath::testadd[小数和整数] PASSED [ 33%] test_math.py::TestMath::testminus[两个小数] PASSED [ 44%] test_math.py::TestMath::testminus[两个整数] PASSED [ 55%] test_math.py::TestMath::testminus[小数和整数] PASSED [ 66%] test_math.py::TestMath::testchengfa[两个小数] FAILED [ 77%] test_math.py::TestMath::testchengfa[两个整数] FAILED [ 88%] test_math.py::TestMath::testchengfa[小数和整数] FAILED [100%]
漂亮很多
添加参数scope='module'运行查看结果,运行顺序有变化,说明默认为scope='function',pytest框架寻找每个方法调用参数,scope='class',pytest框架寻找每个类调用参数,以此类推
test_math.py::TestMath::testadd[两个小数] PASSED [ 11%] test_math.py::TestMath::testminus[两个小数] PASSED [ 22%] test_math.py::TestMath::testchengfa[两个小数] FAILED [ 33%] test_math.py::TestMath::testadd[两个整数] PASSED [ 44%] test_math.py::TestMath::testminus[两个整数] PASSED [ 55%] test_math.py::TestMath::testchengfa[两个整数] FAILED [ 66%] test_math.py::TestMath::testadd[小数和整数] PASSED [ 77%] test_math.py::TestMath::testminus[小数和整数] PASSED [ 88%] test_math.py::TestMath::testchengfa[小数和整数] FAILED [100%]