发布于2026-07-18 阅读(0)
扫一扫,手机访问
在 pytest 中,后置清理代码能不能确保执行,一直是测试编写中的关键问题。测试通过时还好说,一旦断言失败或抛出异常,清理逻辑是否还能可靠触发,就得看机制选得对不对。这里梳理几种主流做法,从最推荐的到极端场景的,一一说明。

在 fixture 的 yield 之后写清理代码,pytest 会在测试结束后自动执行它,无论测试是断言失败还是抛出异常。这几乎是目前最推荐的方式。
import pytest
@pytest.fixture
def db_connection():
# 前置:建立连接
conn = create_connection()
yield conn
# 后置:一定会执行(除非 fixture 前置阶段就挂了)
conn.close()
def test_query(db_connection):
assert db_connection.query("SELECT 1") == 1
有一点需要留意:如果 yield 之前的代码(比如建立连接)就抛了异常,那么 yield 之后的清理代码不会执行。这其实很合理——资源根本没获取到,自然也不需要释放。
功能和 yield 类似,但通过注册回调函数的方式明确声明清理逻辑,适合某些需要动态注册的场景。
import pytest
@pytest.fixture
def db_connection(request):
conn = create_connection()
def cleanup():
conn.close()
request.addfinalizer(cleanup) # 注册后置函数
return conn
同样,addfinalizer 注册成功之后,清理函数一定会执行。
pytest 对经典的 xUnit 风格支持得也很好,如果你习惯这套写法,完全可以用。
方法级:def teardown_method(self, method):
类级:def teardown_class(cls):
模块级:def teardown_module(module):
class TestDatabase:
def setup_method(self):
self.conn = create_connection()
def teardown_method(self):
self.conn.close() # 每个测试方法结束后一定会执行
def test_query(self):
assert self.conn.query("SELECT 1") == 1
如果你有必须回收的全局资源,比如临时文件、进程等,就算 fixture 初始化阶段失败也得清理,可以在 fixture 里用 try/finally。
@pytest.fixture
def temp_file():
f = None
try:
f = open("/tmp/test.txt", "w")
yield f
finally:
if f:
f.close()
# 或者无条件删除文件
这种情况其实比较少见,毕竟大多数场景下,设置失败时资源还没分配,根本不需要清理。
| 方式 | 是否保证后置执行 | 备注 |
|---|---|---|
yield fixture | ✅(前置成功时) | 最推荐,代码简洁 |
request.addfinalizer | ✅(前置成功时) | 与 yield 等价 |
teardown_* 方法 | ✅(对应作用域) | 适用于传统风格 |
try/finally | ✅(无条件) | 适合必须清理的场景 |
除了上面几种核心方式,pytest 还提供了不少其他钩子,覆盖各种粒度的控制需求。
setup_function / teardown_function(函数级别)
适用于独立函数形式的测试用例。
def setup_function(function):
print("\n[前置] 在每个测试函数前执行")
def teardown_function(function):
print("[后置] 在每个测试函数后执行")
def test_add():
assert 1 + 1 == 2
def test_multiply():
assert 2 * 3 == 6
setup_method / teardown_method(类内方法级别)
适用于测试类中的每个测试方法。
class TestMath:
def setup_method(self):
print("\n[前置] 每个测试方法前执行")
def teardown_method(self):
print("[后置] 每个测试方法后执行")
def test_add(self):
assert 1 + 1 == 2
def test_multiply(self):
assert 2 * 3 == 6
setup_class / teardown_class(类级别)
整个测试类只执行一次前置和后置,适合类级别的资源初始化与清理。
class TestDatabase:
@classmethod
def setup_class(cls):
print("\n[前置] 整个测试类只执行一次")
@classmethod
def teardown_class(cls):
print("[后置] 整个测试类只执行一次")
def test_connection(self):
assert True
def test_query(self):
assert True
setup_module / teardown_module(模块级别)
在整个模块(即一个文件)的所有测试前后执行一次。
# test_example.py
def setup_module(module):
print("\n[前置] 模块级别,执行一次")
def teardown_module(module):
print("[后置] 模块级别,执行一次")
def test_a():
assert True
def test_b():
assert True
@pytest.fixture:最推荐、最强大
fixture 是 pytest 的核心功能,可以实现任意粒度的前后置,支持依赖注入、作用域控制、自动清理等。
基本用法(函数级)
import pytest
@pytest.fixture
def db_connection():
print("\n[前置] 建立数据库连接")
conn = {"host": "localhost", "port": 3306}
yield conn # 将资源传递给测试函数
print("[后置] 关闭数据库连接")
def test_query(db_connection):
assert db_connection["port"] == 3306
作用域控制
通过 scope 参数控制生命周期:
| scope | 作用域 |
|---|---|
function | 每个测试函数(默认) |
class | 每个测试类 |
module | 每个模块文件 |
package | 每个包 |
session | 整个测试会话 |
@pytest.fixture(scope="module")
def shared_resource():
print("\n[前置] 模块级别共享资源")
yield {"data": "shared"}
print("[后置] 清理共享资源")
自动使用(autouse=True)
无需在测试参数中显式引用,fixture 会在对应作用域自动执行。
@pytest.fixture(autouse=True)
def auto_log():
print("\n[自动前置] 记录测试开始")
yield
print("[自动后置] 记录测试结束")
def test_something():
assert True # 前后置会自动执行
使用 request 对象获取上下文
@pytest.fixture
def resource(request):
print(f"测试函数名: {request.node.name}")
# 可访问 module, cls, function 等
yield
print("清理")
pytestmark 模块级标记
可以在模块顶部定义 pytestmark,将 fixture 应用到整个模块。
import pytest
pytestmark = pytest.mark.usefixtures("db_connection")
def test_1():
pass
def test_2():
pass
conftest.py 共享 fixture
将 fixture 定义在 conftest.py 文件中,可被多个测试文件共享,适合全局性的资源配置。
# conftest.py
import pytest
@pytest.fixture(scope="session")
def global_config():
return {"env": "test"}
# test_foo.py
def test_config(global_config):
assert global_config["env"] == "test"
在一个测试函数中,各钩子的执行顺序是这样的:
setup_module
setup_class
setup_method
setup_function (对函数测试有效)
<测试函数体>
teardown_function
teardown_method
teardown_class
teardown_module
而 fixture 的 yield 前后代码分别对应前置和后置,且支持嵌套依赖,组合使用效果更佳。
推荐实践:
setup_method / teardown_method 或 setup_function 就够用。@pytest.fixture,因为它更灵活、支持依赖注入、作用域可控,也更符合 pytest 的设计哲学。conftest.py 配合 scope="session",干净利落。合理运用这些机制,测试代码写起来会清晰、高效,维护起来也省心不少。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8