Fixtures 是 pytest 中一种强大的功能,主要用于设置测试前的预条件,以及在测试后进行清理。
@pytest.fixture(scope = "function",params=None,autouse=False,ids=None,name=None)
scope:用于控制Fixture的作用范围,
默认取值为function(函数级别),控制范围的排序为:session > module > class > function
function:每个测试函数都会得到一个新的 fixture 实例(默认作用域)。
class:每个测试类只实例化一次 fixture,对类中的所有测试方法共享。
module:每个模块只实例化一次 fixture,对模块中的所有测试函数共享。
session:每个测试会话只实例化一次 fixture,对所有测试共享。
params:参数允许您为 fixture 指定一组参数,pytest 将为这些参数的每个元素生成一次测试。
在这个例子中,test_param_fixture 将针对每个 params 中的值运行一次。
@pytest.fixture(params=[1, 2, 3])
def param_fixture(request):
return request.param
def test_param_fixture(param_fixture):
assert param_fixture in [1, 2, 3]
- 最基本的使用方法是将 fixture 直接作为测试函数的参数。这种情况下,pytest 在调用测试函数时会自动处理 fixture。
@pytest.fixture
def sample_fixture():
return "some data"
def test_example(sample_fixture):
assert sample_fixture == "some data"
- 使用 usefixtures 装饰器
如果不需要直接访问 fixture 返回的值,但仍然需要执行 fixture(例如设置环境),可以使用 @pytest.mark.usefixtures 装饰器。
@pytest.fixture
def setup_environment():
# 设置环境
print("Setting up environment")
yield
# 清理环境
print("Tearing down environment")
@pytest.mark.usefixtures("setup_environment")
def test_environment():
# 这里不需要直接使用 setup_environment 返回的任何值
assert True
- 作为类的属性
如果在测试类中使用 fixture,并且希望在多个测试方法中共享相同的 fixture 实例,可以将 fixture 作为类的一个属性。
@pytest.fixture(scope="class")
def class_fixture():
return "class data"
@pytest.mark.usefixtures("class_fixture")
class TestClass:
def test_one(self):
assert self.class_fixture == "class data"
def test_two(self):
assert self.class_fixture == "class data"
- 使用 conftest.py
可以在 conftest.py 文件中定义通用的 fixture,这样它们就可以在多个测试文件中使用,而无需显式导入。
# conftest.py
@pytest.fixture
def global_fixture():
return "global data"
# test_example.py
def test_global(global_fixture):
assert global_fixture == "global data"
- Fixture 的参数化
可以参数化 fixture 本身,通过 params 属性为 fixture 提供不同的输入值。
@pytest.fixture(params=[1, 2, 3])
def param_fixture(request):
return request.param
def test_param_fixture(param_fixture):
assert param_fixture in [1, 2, 3]
- 依赖于其他 fixtures
Fixture 可以使用其他 fixtures 作为它们的输入。
@pytest.fixture
def base_fixture():
return "base data"
@pytest.fixture
def dependent_fixture(base_fixture):
return f"dependent on {base_fixture}"
def test_dependent_fixture(dependent_fixture):
assert dependent_fixture == "dependent on base data"
- 使用参数autouse调用fixture
当设置了 autouse=True,该 fixture 将自动应用于同一作用域中的所有测试,这对于需要在许多测试中共用的初始化和清理任务特别有用。
@pytest.fixture(autouse=True)
def setup_and_teardown():
# 这里可以进行测试前的设置
print("Setting up for a test")
yield
# 这里可以进行测试后的清理
print("Tearing down after a test")
def test_example():
assert True # 这个测试会自动使用 setup_and_teardown fixture
@pytest.fixture(autouse=True)
def setup_and_teardown():
print("Setting up")
yield
print("Tearing down")
class TestClass:
def test_one(self):
assert True # 这里会自动使用 setup_and_teardown fixture
def test_two(self):
assert True # 这里也会自动使用 setup_and_teardown fixture
在这个例子中,setup_and_teardown fixture 将自动应用于 TestClass 类中的所有测试方法。
# conftest.py
@pytest.fixture(autouse=True)
def global_setup():
print("Global setup for all tests")
yield
print("Global teardown after all tests")
# test_example.py
def test_something():
assert True # 这里会自动使用 global_setup fixture
在这种情况下,global_setup fixture 将在所有测试开始前自动执行,无论它们定义在哪个文件中。
pytest.mark.parametrize: 装饰器允许您为单个测试函数提供多组参数和值,以便进行多次独立测试。
@pytest.mark.parametrize("x, y, expected", [(1, 1, 2), (2, 3, 5)])
def test_add(x, y, expected):
assert x + y == expected
pytest.mark.parametrize 读取yml文件
@pytest.mark.parametrize('args', yaml.safe_load(open(dataPath, encoding='utf-8'))['instances_test'])
- 配置和初始化
pytest_configure(config):在命令行参数解析之后,测试运行开始之前调用。
pytest_addoption(parser):添加自定义命令行选项。
- 收集测试用例
pytest_collection_modifyitems(session, config, items):在测试用例收集完成后调用,可以用来修改收集到的测试项。
pytest_itemcollected(item):当一个测试项被收集时调用。
- 设置和清理
pytest_fixture_setup(fixturedef, request):在执行 fixture 前调用。
pytest_fixture_post_finalizer(fixturedef, request):在 fixture 被清理后调用。
- 测试执行
pytest_runtest_protocol(item, nextitem):自定义测试执行的协议。
pytest_runtest_setup(item):在每个测试项的 setup 阶段调用。
pytest_runtest_call(item):在调用测试函数时调用。
pytest_runtest_teardown(item, nextitem):在每个测试项的 teardown 阶段调用。
pytest_runtest_logreport(report):在生成测试报告后调用。
- 异常处理
pytest_exception_interact(node, call, report):在测试中出现异常时调用。
- 报告和日志
pytest_terminal_summary(terminalreporter, exitstatus, config):在测试运行结束时,生成终端总结报告。
pytest_report_header(config, startdir):添加额外的头部信息到测试报告中。
pytest_report_teststatus(report, config):自定义测试的状态报告(如 pass, fail, skip)。
- 会话开始和结束
pytest_sessionstart(session):在会话开始时调用。
pytest_sessionfinish(session, exitstatus):在会话结束时调用。
第一种用法: 装饰器 @pytest.mark.repeat(次数)
@pytest.mark.repeat(5)
第二种用法: 命令行参数
pytest --count=5 test.py
第三种用法: 结合repeat-scope运行
pytest.main(['-sv','--count=2','--repeat-scope=session',__file__])
在测试代码中,您可以使用 pytest.assume() 替代标准的 assert 语句。这样,即使某个假设失败,测试也会继续执行,直到所有的假设都被检查完毕。
import pytest
def test_multiple_assumptions():
pytest.assume(1 == 2, "One is not equal to Two")
pytest.assume(True, "This assumption is True")
pytest.assume(3 == 3, "Three is equal to Three")
测试结果会显示所有断言的状态。失败的假设会被标记出来,但它们不会阻止测试的其他部分执行。
@pytest.mark.run(order=[number])
主要解决用例之间的依赖关系。如果依赖的上下文失败后续的用例会被标识为跳过执行,相当于执行了 pytest.mark.skip
需要使用装饰器来标记测试用例的依赖关系。
import pytest
@pytest.mark.dependency()
def test_a():
assert True
@pytest.mark.dependency(depends=["test_a"])
def test_b():
pass
在上述例子中,test_b 依赖于 test_a 的成功执行。如果 test_a 失败,test_b 将被自动跳过。
使用分组来管理依赖
import pytest
@pytest.mark.dependency(name="group1")
def test_c():
assert True
@pytest.mark.dependency(name="group2")
def test_d():
assert True
@pytest.mark.dependency(depends=["group1", "group2"])
def test_e():
pass
在这个例子中,test_e 依赖于 test_c 和 test_d 的成功。如果任何一个测试失败,test_e 将被跳过。
使用方法一: 装饰器
@pytest.mark.flaky(reruns=50,reruns_delay=2) #重跑50次,每次间隔2s
使用方法二: 命令行
pytest.main(['-sv','--reruns=2','--reruns-delay=2',__file__])
它允许你以分布式方式并行运行测试。这意味着你可以在多个CPU核心上同时运行测试,或者在不同的机器上分布测试,从而显著减少测试运行的总时间。
并行在多个CPU核心上运行
pytest -n 4
知道某个测试由于某些原因(如尚未修复的 bug、未实现的功能或依赖于外部系统的测试)将会失败,但您仍希望包含此测试在测试套件中时,可以使用 pytest.mark.xfail。
import pytest
@pytest.mark.xfail
def test_example():
assert False
在这个例子中,test_example 测试预期会失败。
import pytest
import sys
@pytest.mark.xfail(sys.version_info < (3,8), reason="requires python3.8 or higher")
def test_example():
assert True
在这个例子中,只有当 Python 版本低于 3.8 时,test_example 测试才预期失败。
访问 xfail 的结果
您可以在测试报告中看到哪些测试被标记为失败,以及它们的原因。