简介
-
@pytest.fixture()修饰器用于声明函数是一个fixture
-
fixture 在其他函数,模块,类或整个工程调用它时会被激活并优先执行
-
fixture 通常会被用于完成测试前后进行预备、清理工作和重复操作(例如:定义测试数据集 、配置系统初始状态 、提供测试数据 、测试前后预备清理工作)
-
用法用法 @pytest.fixture(scope=xxx,autouse=True,name=‘newname’,params=参数名,ids=idname)
通过conftest.py共享fixture
-
conftest.py
-
是python模块,但是不可被测试文件导入,import conftest 的用法是不允许出现的。
-
被pytest视作一个本地插件库。可以看做是供conftest.py所在目录及其子目录下所有测试使用的仓库。
-
-
共享fixture
-
多文件共享fixture
如果你希望多个测试文件共享fixture,可以在某个公共目录下新建一个conftest.py,将fixture放在其中。 -
某文件独享fixture
如果你希望某个测试文件独享fixture,那么将fixture放在这个文件里面。或者将fixture放在一个和需要独享fixture测试文件同级的conftest.py中 -
示例:
test_cases_pytest.conftest.py:
import pytest @pytest.fixture() def fuc_conftest(): print( '我定义在test_cases_pytest.conftest.py的fixture,作用域未function,可以供test_cases_pytest路径下及其子路径下所有函数调用' )
test_cases_pytest.test_fixtures.py:
import pytest def id_func(fixture_value): """A function for generating ids.""" t = fixture_value return 'id_' + str(t) @pytest.fixture(params=[1, 2, 3],ids=id_fuc) def need_data(request): # 传入参数request 系统封装参数 return request.param # 取列表中单个值,默认的取值方式 def test_a1(need_data): print("------->test_a")
运行
pytest -s -v .\test_fixtures.py::test_a1
输出结果:
-
使用fixture实现steup 和teardown
举例:
测试开始前需要配置并连接数据库(已使用test_start_demo_db()进行实现)
测试完成后需要清理数据并断开数据库连接(已使用test_stop_demo_db()进行实现)
示例:
@pytest.fixture()
def demo_db(tmpdir):
# stetup: 连接数据库
demo.test_start_demo_db()
print("已连接数据库")
yield # 运行测试函数
# teardom:清理数据并断开数据库连接
demo.test_stop_demo_db()
print("数据库连接关闭")
def test_a(demo_db): # ️ test_a方法中以变量的形式传入了被fixture标识的函数名
print("连接成功数据库后的一系列测试动作")
运行结果:
若setup部分的代码,出现错误或断言失败,yield的代码和其后的代码将不会再执行,导致无法测试环境没有进行清理。可使用request的终结函数(request.addfinalizer())实现测试后的清理
可以使用**–setup-show** 参数查看fixture的回溯信息
使用fixture传递测试数据
@pytest.fixture()
def some_list():
yield [1, 'ss', None, {'bb': 33}]
def test_some_list(some_list):
assert some_list == [1, 'ss', None, {'bb': 33}]
assert some_list[3]['bb'] == 33
运行结果:
使用多个fixture
例如:在测试开始前在数据库中构造测试数据
fixtrue:
数据库连接和断开连接:demo_db()
要添加的数据集合:demo_3_datas()
添加数据: demo_db_add_3_datas(demo_db,demo_3_datas)
测试调用:test_add_increases_count(demo_db_add_3_datas)
在测试test_add_increases_count前,会先连接数据库并将数据集合中的3条数据添加到数据库
使用示例代码如下:
@pytest.fixture()
def demo_db(tmpdir):
demo.test_start_demo_db()
yield # 运行测试函数
demo.test_stop_demo_db()
@pytest.fixture()
def demo_3_datas():
print('返回包含3条数据的list')
@pytest.fixture()
def demo_db_add_3_datas(demo_db, demo_3_datas):
for data in demo_3_datas:
print('逐条添加demo_3_datas中的数据')
demo.add(data)
def test_add_increases_count(demo_db_add_3_datas):
# 可以使用数据库中的数据,比如断言条数是否为3
assert demo.count() == 3
以上只是使用思路的示例,若正常实现的话运行结果会是pass。
使用usefixtures指定fixture
用法:@pytest.mark.usefixtures(‘fixture1’,‘fixture2’)
@pytest.fixture()
def some_list():
yield [1, 'ss', None, {'bb': 33}]
@pytest.mark.usefixtures('some_list')
def test_some_list2():
assert some_list == [1, 'ss', None, {'bb': 33}]
assert some_list[3]['bb'] == 33
这与在测试函数中添加fixture参数大体上差不多,但是使用usefixtures不能使用fixture的返回值,比如上代码运行后会failed:
使用scope指定fixture的作用范围
scope 是fixture的一个可选参数,用来控制fixture执行配置和销毁逻辑的频率。
- scope =function(默认值):每个测试函数只运行一次
- scope =class: 每个测试类只运行一次,类里面的测试方法或fixture共享此fixture
- scope =module: 每个模块只运行一次,模块中的测试函数或者其他fixture均可共享此fixture
- scope =session: 每次会话只运行一次,一次pytest会话中的所有测试函数,方法都共享此fixture
"""Demo fixture scope."""
import pytest
@pytest.fixture(scope='function')
def func_scope():
"""A function scope fixture."""
@pytest.fixture(scope='module')
def mod_scope():
"""A module scope fixture."""
@pytest.fixture(scope='session')
def sess_scope():
"""A session scope fixture."""
@pytest.fixture(scope='class')
def class_scope():
"""A class scope fixture."""
def test_1(sess_scope, class_scope, mod_scope, func_scope):
"""Test using session, module, and function scope fixtures."""
def test_2(sess_scope, class_scope, mod_scope, func_scope):
"""Demo is more fun with multiple tests."""
@pytest.mark.usefixtures('class_scope', 'mod_scope', 'sess_scope','func_scope')
class TestSomething():
"""Demo class scope fixtures."""
def test_3(self):
"""Test using a class scope fixture."""
def test_4(self):
"""Again, multiple tests are more fun."""
运行结果:
注意:fixture 只能使用同级别的其他fixture,或比自己级别更高的其他fixture,比如:函数级别的fixture可以使用同级别的,也可以使用类级别,模块级别和会话级别的fixture,但是反过来就不行。
修改fixture的作用范围
比如在测试之前进行数据连接,目前为止的示例里面都是每次测试都去连接并创建数据,我们可以把数据库操作的作用范围改成会话级别即可
@pytest.fixture(scope='session')
def demo_db(tmpdir):
demo.test_start_demo_db()
yield # 运行测试函数
demo.test_stop_demo_db()
@pytest.fixture(scope='session')
def demo_3_datas():
print('返回包含3条数据的list')
@pytest.fixture()
def demo_db_add_3_datas(demo_db, demo_3_datas):
for data in demo_3_datas:
print('逐条添加demo_3_datas中的数据')
demo.add(data)
def test_add_increases_count(demo_db_add_3_datas):
# 可以使用数据库中的数据,比如断言条数是否为3
assert demo.count() == 3
指定 fixture autouse=True
通过指定autouse=True ,使作用域内的函数都运行该 fixture,并且在同级别的测试函数中会优先执行
@pytest.fixture(autouse=True) # 设置为默认运行
def before():
print("------->before")
class Test_ABC:
def setup(self):
print("------->setup")
def test_a(self):
print("------->test_a")
assert 1
if __name__ == '__main__':
pytest.main("-s test_abc.py")
运行结果:
------->before # 发现before自动优先于测试类运行,因为autouse为 True 会默认在相同作用域下优先执行
------->setup
------->test_a
fixture 重命名
@pytest.fixture(name='t')
def some_tuple():
print('some_tuple setup ')
yield (1, 3, 5)
print('some_tuple teardown ')
def test_some_tuple(t):
assert some_tuple == (1, 3, 5)
可以使用pytest --fixtures .\test_fixtures.py
查看测试文件中可供使用的fixture
fixture 参数化
import pytest
@pytest.fixture(params=[1, 2, 3])
def need_data(request): # 传入参数request 系统封装参数
return request.param # 取列表中单个值,默认的取值方式
def test_a1(need_data):
print("------->test_a")
request 是pytest内建的fixture之一,代表fixture的调用状态,它有一个param字段,会被@pytest.fixture(params=[1, 2, 3])的params列表中的一个元素填充。
运行pytest -s -v .\test_fixtures.py::test_a1
输出结果:
可以发现结果运行了三次
对测试函数进行参数化处理,可以多次运行的知识该测试函数,但是参数化fixture使每个使用该fixture的测试函数都可以被多次运行。
指定fixture ids
ids参数也可被指定为一个函数,供pytest生成task标记
import pytest
def id_func(fixture_value):
"""A function for generating ids."""
t = fixture_value
return 'id_' + str(t)
@pytest.fixture(params=[1, 2, 3],ids=id_fuc)
def need_data(request): # 传入参数request 系统封装参数
return request.param # 取列表中单个值,默认的取值方式
def test_a1(need_data):
print("------->test_a")
运行pytest -s -v .\test_fixtures.py::test_a1
输出结果: