pytest-fixture
fixture
pytest-fixture
@pytest.fixture() 装饰器用于声明函数是一个fixture,该fixture的名字默认为函数名,也可以自己指定名称(通过name参数可指定别名);如果测试用例的参数列表中包含fixture的名字,那么pytest会根据名字检测到该fixture,并在测试函数运行之前执行该fixture。
@pytest.fixture(scope="function", params=None, autouse=False, ids=None, name=None)
参数:
session:每次会话只需要运行一次,会话内所有模块、类、方法,都共享这个fixture
module:每一个.py文件调用一次
class:每一个类中调用一次
function:每一个function或者类方法中都会调用
动态作用域:
在某些情况下,您可能希望更改fixture的作用域而不更改代码。为此,将一个可调用对象传递给scope。该可调用对象必须返回一个具有有效作用域的字符串,并且只会执行一次——在fixture定义期间。它将使用两个关键字参数调用——fixture_name作为字符串,config使用配置对象。
这在处理需要时间安装的fixture时特别有用,比如生成一个docker容器。您可以使用命令行参数来控制派生容器在不同环境下的作用域。
def determine_scope(fixture_name, config):
if config.getoption("--keep-containers", None):
return "session"
return "function"
@pytest.fixture(scope=determine_scope)
def docker_container():
yield spawn_container()
request.param。①Fixture可选的参数列表,支持列表传入
②默认为None,每个param的值。
③可通过request.param接受设置的返回值,params中有多少个元素,在测试时,引用次fixture的函数就会调用几次。
④可与参数ids一起使用,作为每个参数的标识。
# -*- coding: utf-8 -*-
import pytest
@pytest.fixture(scope="function", params=["hello", "hi", 123])
def ddt(request): # 通过request接收params中的参数
print("=====setup======")
yield request.param # 通过request.param获取当前使用的参数
print("=====teardown======")
class TestDemo:
def test_one(self):
print("---------hello world--------")
# 因autouse为False,故需显示引用,即测试函数可以直接使用fixture名称作为输入参数
def test_two(self, ddt):
print(f"--------{ddt}-------")
if __name__ == '__main__':
pytest.main("-vs")

自动执行fixture,无需在测试函数使用fixture名称作为输入参数。如果为False(默认值),则需要显式引用(测试函数可以直接使用fixture名称作为输入参数)。1)参数传参:
2)装饰器传参:
fixture可相互调用,但要注意:如果级别不同,低级别可以调用高级别,高级别不能调用低级别。
影响fixture实例化顺序的三个因素是:
import pytest
@pytest.fixture(scope="session")
def order():
return []
@pytest.fixture
def func(order):
order.append("function")
@pytest.fixture(scope="class")
def cls(order):
order.append("class")
@pytest.fixture(scope="module")
def mod(order):
order.append("module")
@pytest.fixture(scope="package")
def pack(order):
order.append("package")
@pytest.fixture(scope="session")
def sess(order):
order.append("session")
class TestClass:
def test_order(self, func, cls, mod, pack, sess, order):
assert order == ["session", "package", "module", "class", "function"]
六个fixture函数:
order:scope为session级别,返回一个空list。
func: 调用了order,scope为默认的function级别,并实现向order返回的列表中插入”function“的操作。
cls:调用了order,scope为class级别,并实现向order返回的列表中插入”class“的操作。
mod:调用了order,scope为module级别,并实现向order返回的列表中插入”module“的操作。
pack:调用了order,scope为package级别,并实现向order返回的列表中插入”package“的操作。
sess:调用了order,scope为session级别,并实现向order返回的列表中插入”session“的操作。
结论:
1.fixture的scope级别越高,那么它执行的优先级越高。优先级为:session>package>module>class>function
2.fixture如果存在依赖项,那么它所依赖的fixture函数会先被执行。
3.同scope级别fixture中,自动使用的fixture会最先执行;若该fixture存在依赖项,则对于调用了fixture的测试函数来说,这些依赖项也可以看做是自动使用的。
fixture写在测试文件中:
import pytest
@pytest.fixture()
def fix_add(request):
print("fixture拿到的原始参数是:", request.param)
sum = request.param[0] + request.param[1]
yield sum
@pytest.mark.parametrize("fix_add", [(1, 3), (2, 4)], indirect=True)
def test_add(fix_add):
print('-----执行fix_add测试用例------')
print(f"fixture返回的参数和是:{fix_add}")

一个工程下可以有多个conftest.py的文件,在工程根目录下设置的conftest文件起到全局作用。在不同子目录下也可以放conftest.py的文件,作用范围只能在该层级以及以下目录生效,另conftest是不能跨模块调用的。在conftest.py定义的fixture不需要进行import,pytest会自动查找使用。
# -*- coding: utf-8 -*-
import pytest
from logic.gen.account.manager import AccountManager
from util.context.context import Context
def pytest_addoption(parser):
# 自定义命令行参数
parser.addoption('--passenger', action='store', help='passenger account')
parser.addoption('--driver', action='store', help='driver account')
@pytest.fixture
def replace_accounts(request):
# 获取命令行参数的值
passenger = request.config.getoption('--passenger')
driver = request.config.getoption('--driver')
print(f"----------{passenger}---{driver}----------")
return (
AccountManager(ctx=Context(), account=passenger),
AccountManager(ctx=Context(), account=driver))
测试文件:
class TestDemo:
# 因autouse为False,故需显示引用,即测试函数可以直接使用fixture名称作为输入参数
def test_one(self, replace_accounts):
p, d = replace_accounts[0], replace_accounts[1]
print(f"==========={type(p)}===========")
print(f"==========={d}===========")
执行:python3 -m pytest -vs --passenger HXZPassenger --driver HXZDriver test_666.py

import importlib
import pytest
@pytest.fixture
def replace_accounts(request):
case_name = request.param.get('case_name')
passenger_phone = request.param.get('passenger_phone')
driver_phone = request.param.get('driver_phone')
module_name = "tests.test_{}".format(case_name)
test_module = importlib.import_module(module_name) # 通过importlib模块中的import_module函数动态导入指定测试用例模块
setattr(test_module, "passenger_phone", passenger_phone) # 用setattr函数来动态修改测试用例中的参数值
setattr(test_module, "driver_phone", driver_phone)
return test_module.test_case # fixture返回修改后的测试用例函数