You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

pytest fixture创建原理及生成器函数foo的测试方法问询

问题描述

我定义了一个生成器函数foo,代码如下:

def foo():
    resource = setup()
    yield resource
    tidy(resource)

将其作为pytest fixture使用:

@pytest.fixture
def foofix():
    yield from foo()

该fixture运行正常。我想要测试foo函数,编写了测试代码:

def test_foo():
    res_gen = foo()
    res = next(res_gen)
    assert res.is_working

但调用next后,resource会被立即清理,导致无法断言其是否正常工作。想了解pytest是如何在清理前使用resource的,以及我该如何实现相同的测试方式?

原理与解决方案

pytest的工作机制

pytest处理生成器类型的fixture时,会先执行yield之前的代码获取资源,然后把资源注入给测试函数使用。只有当测试函数执行完毕后,pytest才会继续执行生成器中yield之后的清理代码(也就是tidy(resource))。这就是fixture能正常使用资源而不会提前被清理的核心原因。

测试foo函数的正确方式

要测试foo,你需要在完成断言后,手动驱动生成器执行剩余的清理代码,或者用上下文管理器模拟pytest的逻辑:

方法1:手动控制生成器执行流程

def test_foo():
    res_gen = foo()
    res = next(res_gen)
    try:
        assert res.is_working  # 在清理前完成断言逻辑
    finally:
        try:
            next(res_gen)  # 触发生成器执行清理代码
        except StopIteration:
            pass  # 生成器执行完毕抛出该异常属于正常情况,无需处理

用try/finally结构可以确保无论断言是否失败,清理代码都会被执行,避免资源泄漏。

方法2:封装成上下文管理器(更优雅)

把生成器转换成上下文管理器,模拟fixture的使用体验:

from contextlib import contextmanager

@contextmanager
def foo_context():
    gen = foo()
    resource = next(gen)
    try:
        yield resource
    finally:
        try:
            next(gen)
        except StopIteration:
            pass

def test_foo():
    with foo_context() as res:
        assert res.is_working

这种写法和使用fixture的逻辑一致,上下文管理器会自动在代码块结束后执行清理逻辑。

内容的提问来源于stack exchange,提问作者joel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 19:07:37