2. Test Fixture 概念与作用:Fixture 生命周期、setup/teardown、资源隔离原则
2.1 什么是 Test Fixture?
Test Fixture(测试夹具)是测试执行所需的固定环境或预置状态。它帮助你将测试逻辑与环境准备、清理代码分离,使每个测试用例都从一个可预测、可重复的起点开始。Fixture 可以是临时数据库、模拟服务器、文件系统快照、配置对象或任何测试依赖的资源。
核心作用:消除测试间的状态泄漏,提高测试的隔离性与可靠性。
2.2 Fixture 生命周期
Fixture 的生命周期由三个阶段组成:Setup(建立) → Test(测试执行) → Teardown(清理)。现代测试框架(如 pytest、JUnit、unittest)通过装饰器或作用域来控制 fixture 的创建与销毁时机。
| 阶段 | 行为 | 典型操作 |
|---|---|---|
| Setup | 在测试用例执行前运行 | 创建数据库连接、初始化模拟对象、写入临时文件 |
| Test | 注入 fixture 返回的资源 | 使用预置数据进行断言、调用被测函数 |
| Teardown | 测试结束后(无论是否失败)运行 | 关闭连接、删除临时文件、还原全局状态 |
2.3 Fixture 作用域
框架通常支持多种作用域,控制 fixture 的重用范围:
- Function(函数级):每个测试函数独立创建/销毁,最严格的隔离。
- Class(类级):测试类内所有方法共享一个实例。
- Module(模块级):同一模块内所有测试共享。
- Session(会话级):整个测试会话只创建一次,用于昂贵资源。
实践经验:优先使用 function 作用域,只在资源创建成本极高(如 Docker 容器)时才升级作用域,并务必实现线程/进程安全。
2.4 Setup / Teardown 实现模式
以 Python unittest 和 pytest 为例展示两种常见实现:
2.4.1 unittest 中的 setUp / tearDown
import unittest
class TestDatabase(unittest.TestCase):
def setUp(self):
# Setup:创建数据库连接
self.db = Database.connect(":memory:")
self.db.create_table("users")
def tearDown(self):
# Teardown:关闭连接
self.db.close()
def test_insert_user(self):
self.db.insert("user_1")
self.assertEqual(self.db.count("users"), 1)
2.4.2 pytest 中的 fixture(yield 方式)
import pytest
@pytest.fixture
def db():
# Setup
db = Database.connect(":memory:")
db.create_table("users")
yield db # 测试执行
# Teardown
db.close()
def test_insert_user(db):
db.insert("user_1")
assert db.count("users") == 1
2.5 资源隔离原则
资源隔离是 fixture 设计的核心目标之一,防止测试间通过共享状态产生耦合。隔离原则包括:
- 每个测试拥有独立的 fixture 实例(除非显式声明共享)。
- Teardown 必须能够恢复全局环境(环境变量、文件系统、网络端口等)。
- 使用随机或唯一标识符避免命名冲突(如临时目录名加 uuid)。
- 对不可隔离的资源(如硬件)使用 mock 或 fake 替代。
常见错误:在 fixture 中修改全局配置而不还原,导致后续测试因状态残留而失败。务必在 teardown 中恢复原始状态。
2.6 Fixture 生命周期与隔离关系图
下图展示了两个测试用例(Test_A 和 Test_B)的 fixture 生命周期及隔离边界:
2.7 常见错误与调试
- Fixture 泄漏:Teardown 未执行或未正确处理异常路径。
- 共享可变对象:在函数级 fixture 中返回列表或字典,测试代码意外修改导致后续测试污染。
- 作用域误用:将需要隔离的资源声明为 session 级,却未做线程安全处理。
- 调试技巧:启用框架的 fixture 日志(如 pytest 的
--setup-show)查看创建/销毁顺序。
# pytest 调试输出示例
pytest --setup-show test_demo.py
SETUP F db
test_demo.py::test_insert_user (fixtures used: db)
TEARDOWN F db
2.8 性能与稳定性优化
- 延迟初始化:只在测试真正依赖时才创建 fixture,避免不必要的开销。
- 缓存昂贵资源:session 级 fixture 配合
scope="session"和conftest.py共享,但务必确保线程安全。 - 禁用自动清理:对极快创建/销毁的资源(如临时字符串)可跳过 teardown,由垃圾回收处理。
- 使用守卫模式:在 setup 中记录异常,teardown 时根据标记执行不同的清理策略。
最佳实践:始终编写与 fixture 配套的测试(fixture 自身的测试),验证 setup 和 teardown 的正确性。使用
pytest.fixture(autouse=True) 时注意不影响其他不期望该 fixture 的测试。