以下是 pytest 中 fixture 的 4 个具体使用案例,涵盖不同应用场景。
案例1:基本 fixture ------ 提供测试数据
python
# test_fixture_basic.py
import pytest
@pytest.fixture
def sample_data():
"""返回一个字典作为测试数据"""
return {"name": "Alice", "age": 30}
def test_user_info(sample_data):
assert sample_data["name"] == "Alice"
assert sample_data["age"] == 30
案例2:fixture 作用域 ------ 控制生命周期
python
# test_fixture_scope.py
import pytest
import time
@pytest.fixture(scope="session")
def shared_resource():
"""整个测试会话只创建一次"""
print("\n[setup] 创建共享资源(耗时操作)")
resource = {"data": [1, 2, 3]}
yield resource
print("\n[teardown] 释放共享资源")
def test_one(shared_resource):
assert shared_resource["data"] == [1, 2, 3]
def test_two(shared_resource):
assert len(shared_resource["data"]) == 3
运行 pytest -s 可看到 setup/teardown 只执行一次。
案例3:fixture 依赖另一个 fixture
python
# test_fixture_dependency.py
import pytest
@pytest.fixture
def user():
return {"id": 1, "name": "Bob"}
@pytest.fixture
def user_with_email(user):
"""依赖 user fixture,扩展数据"""
user["email"] = "bob@example.com"
return user
def test_user_email(user_with_email):
assert user_with_email["name"] == "Bob"
assert user_with_email["email"] == "bob@example.com"
案例4:使用 yield 实现 teardown(资源清理)
python
# test_fixture_teardown.py
import pytest
import tempfile
import os
@pytest.fixture
def temp_file():
"""创建临时文件,测试后自动删除"""
f = tempfile.NamedTemporaryFile(delete=False)
f.write(b"pytest data")
f.close()
yield f.name # 将文件路径传给测试函数
# teardown 部分
os.unlink(f.name)
print(f"\n已删除临时文件 {f.name}")
def test_temp_file_content(temp_file):
with open(temp_file, "rb") as f:
content = f.read()
assert content == b"pytest data"
运行方式 :
保存任意 .py 文件,执行 pytest 文件名.py -v(加 -s 可看到 print 输出)。
补充说明:
- 默认 fixture 作用域为
function(每个测试函数独立) - 可使用
autouse=True让 fixture 自动生效(无需显式引用) - 通过
pytest.mark.usefixtures可在类或模块级别引用多个 fixture
在 unittest 框架中,虽然没有像 pytest 那样独立且高度灵活的 @pytest.fixture 装饰器,但它内置了一套名为 xUnit 风格的 fixture 机制。这套机制通过预定义的方法钩子来实现测试前后的准备和清理工作。
⚙️ unittest 中的"Fixture"核心方法
以下是 unittest 中实现 Fixture 的核心方法,它们作用于不同层级,共同构成了测试的环境管理:
| 层级 | 方法 | 执行时机 | 适用场景 |
|---|---|---|---|
| 方法级 | setUp(self) |
每个 测试方法执行前 | 为每个测试准备独立的环境,如创建临时对象、重置变量状态 |
tearDown(self) |
每个 测试方法执行后 | 清理每个测试产生的临时数据,如关闭网络连接、清空列表 | |
| 类级 | setUpClass(cls) |
整个测试类 的所有方法执行前执行一次 | 执行一次性的、开销较大的准备工作,如建立数据库连接池、加载配置 |
tearDownClass(cls) |
整个测试类 的所有方法执行后执行一次 | 清理类级别的资源,如断开数据库连接池 | |
| 模块级 | setUpModule() |
整个模块文件 中的所有测试执行前执行一次 | 进行模块级的全局环境配置,如设置系统环境变量、启动一个全局服务 |
tearDownModule() |
整个模块文件 中的所有测试执行后执行一次 | 清理模块级的全局资源,如停止全局服务 |
注意 :
setUpClass和tearDownClass是类方法,必须使用@classmethod装饰器。setUpModule和tearDownModule是模块级别的函数,需直接定义在测试文件中。
📝 核心Fixture使用案例
以下示例演示了 setUp(), tearDown(), setUpClass(), tearDownClass() 和 addCleanup() 的使用。
python
import unittest
import os
import tempfile
class TestMathOperations(unittest.TestCase):
@classmethod
def setUpClass(cls):
"""整个测试类只执行一次"""
print("\n[setUpClass] 初始化类级资源")
# 例如:创建数据库连接池
cls.db_pool = "Mock DB Pool"
cls.shared_value = 42
@classmethod
def tearDownClass(cls):
"""整个测试类只执行一次"""
print("[tearDownClass] 清理类级资源")
cls.db_pool = None
def setUp(self):
"""每个测试方法执行前都会执行"""
print(f"\n[setUp] 为测试 '{self._testMethodName}' 准备环境")
self.temp_file = tempfile.NamedTemporaryFile(delete=False)
self.temp_file.write(b"test data")
self.temp_file.close()
# 使用 addCleanup 确保临时文件被删除,即使测试失败也会执行
self.addCleanup(os.remove, self.temp_file.name)
def tearDown(self):
"""每个测试方法执行后都会执行"""
print(f"[tearDown] 清理测试 '{self._testMethodName}' 的环境")
# 这里可以进行一些常规清理,但注意文件清理已由 addCleanup 处理
def test_addition(self):
print(" 执行测试: test_addition")
result = 1 + 1
self.assertEqual(result, 2)
def test_subtraction(self):
print(" 执行测试: test_subtraction")
result = 5 - 3
self.assertEqual(result, 2)
def test_shared_resource(self):
"""演示使用类级共享资源"""
print(f" 执行测试: test_shared_resource, 共享值: {self.shared_value}")
self.assertEqual(self.shared_value, 42)
if __name__ == '__main__':
unittest.main()
运行这个测试,你将在控制台看到清晰的执行顺序,直观理解各钩子方法的调用时机。
💎 总结:pytest Fixture vs. unittest Fixture
| 特性 | pytest Fixture |
unittest Fixture |
|---|---|---|
| 实现方式 | 使用 @pytest.fixture 装饰器声明,高度解耦 |
继承 unittest.TestCase 并重写预定义的钩子方法 |
| 灵活性 | 极高,支持依赖注入、参数化、多种作用域 | 较低,遵循固定的 setUp/tearDown 模式 |
| 代码重用 | 简单,Fixture 可跨文件、跨项目共享 | 较复杂,通常需要通过继承 TestCase 基类来复用 |
| 作用域 | 支持 function, class, module, package, session |
通过 setUp/tearDown (方法)、setUpClass/tearDownClass (类)、setUpModule/tearDownModule (模块) 实现三级作用域 |
| 清理机制 | yield 语句后编写清理代码,简洁直观 |
通过 tearDown 和 addCleanup 实现 |
| 学习曲线 | 稍陡,但概念统一,掌握后效率极高 | 平缓,结构固定,易于理解和入门 |
unittest 的 Fixture 机制简单直接,对于基础的项目测试而言,它的 setUp 和 tearDown 方法已经足够清晰和有效。不过,如果你追求更灵活的测试组织、更高效的资源共享,pytest 的 fixture 会是更强大的选择。