pytest.fixture 是 pytest 的一个重要特性,用于创建和管理测试的前置条件(setup)和后置条件(teardown)。它允许你为测试提供一些预先准备好的资源或数据,而不需要在每个测试函数中重复这些逻辑。
- 基础概念
pytest.fixture 是一个装饰器,标记一个函数作为“夹具”(fixture),这个夹具会在每次测试函数运行前被调用,并可以在测试函数中作为参数使用。测试函数中的夹具会自动注入。
夹具可以用于:
• 生成测试数据。
• 初始化和清理资源(如数据库连接、文件操作等)。
• 共享资源。
- 基础用法
最简单的情况是使用 pytest.fixture 创建一个资源,并在测试函数中作为参数传入:
import pytest
定义一个简单的夹具
@pytest.fixture
def sample_data():
return [1, 2, 3]
# 使用夹具的测试函数
def test_sum(sample_data):
assert sum(sample_data) == 6
在上面的例子中,sample_data 是一个夹具,test_sum 测试函数会接收它作为参数,运行时 sample_data 先执行,返回 [1, 2, 3],然后 test_sum 执行,检查列表的和是否为 6。
- 夹具的作用域
夹具的作用域定义了它的生命周期。pytest 提供了以下几种作用域:
• function(默认作用域):每次调用测试时都会重新创建夹具。
• class:每个测试类中的所有测试用例共享同一个夹具。
• module:每个模块中所有测试用例共享同一个夹具。
• session:整个测试会话中所有测试用例共享同一个夹具,适用于长时间运行的资源(如数据库连接等)。
示例:作用域
import pytest
使用 function 作用域(默认)
@pytest.fixture
def setup_function():
print("\nSetup before each test function")
return "data"
使用 module 作用域
@pytest.fixture(scope="module")
def setup_module():
print("\nSetup before all tests in the module")
return "module_data"
def test_example_1(setup_function, setup_module):
assert setup_function == "data"
assert setup_module == "module_data"
def test_example_2(setup_function, setup_module):
assert setup_function == "data"
assert setup_module == "module_data"
在这个例子中:
• setup_function 会在每个测试函数之前执行。
• setup_module 只会在整个模块中的测试开始之前执行一次。
- 清理(Teardown)
有时你可能需要在测试完成后进行清理操作(比如关闭文件、断开数据库连接等)。你可以通过 yield 和 finalizer 实现清理操作。
示例:使用 yield 进行清理
import pytest
@pytest.fixture
def resource():
print("\nSetting up resource")
resource = {"name": "test_resource"}
yield resource # 测试函数运行前的设置
print("\nCleaning up resource")
resource.clear() # 测试函数运行后的清理
def test_resource_usage(resource):
resource["name"] = "modified"
assert resource["name"] == "modified"
在上面的代码中:
• 在 yield 语句之前的代码执行“setup”操作。
• 在 yield 语句之后的代码执行“teardown”操作。即使测试失败,yield 后的清理操作也会执行。
- 自动使用夹具
你可以通过 autouse=True 自动使夹具在所有测试函数中生效,而不需要显式地将它们作为参数传递。
示例:自动使用夹具
import pytest
@pytest.fixture(autouse=True)
def setup_autouse():
print("\nThis setup is automatically used in all tests")
def test_example_1():
print("Running test_example_1")
def test_example_2():
print("Running test_example_2")
这段代码中,setup_autouse 会自动在每个测试函数执行之前运行。
- 传递参数给夹具
有时你可能希望将一些参数传递给夹具。pytest 提供了参数化夹具的功能,让你能够动态地传递不同的参数给夹具,从而在测试中使用不同的数据。
示例:参数化夹具
import pytest
参数化夹具
@pytest.fixture(params=[1, 2, 3])
def number(request):
return request.param
def test_number(number):
assert number in [1, 2, 3]
每次运行 test_number 时,number 夹具会分别返回 1, 2, 3,从而执行三次测试。
- 使用夹具进行依赖注入
夹具可以相互依赖,即一个夹具可以依赖另一个夹具。pytest 会根据需要自动传递依赖的夹具。
示例:夹具依赖
import pytest
# 定义第一个夹具
@pytest.fixture
def database():
db = {"host": "localhost", "port": 3306}
return db
# 依赖于第一个夹具
@pytest.fixture
def connection(database):
conn = f"Connecting to {database['host']}:{database['port']}"
return conn
def test_connection(connection):
assert connection == "Connecting to localhost:3306"
在这个例子中,connection 夹具依赖于 database 夹具。pytest 会自动传递 database 夹具的返回值给 connection。
-
总结
• pytest.fixture 是用于为测试提供“前置条件”和“后置条件”的工具。
• 夹具可以帮助你在多个测试中共享资源,减少代码重复。
• 你可以控制夹具的生命周期(作用域),并且在测试结束后清理资源。
• 通过 yield 和 autouse 可以更灵活地管理资源和清理过程。
• 参数化夹具允许测试在不同的数据集上执行。
使用夹具能够极大地提高测试的可维护性和可复用性,尤其是在测试数据初始化和资源管理上。