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

在pytest Fixture中为何使用yield而非return?含无清理场景疑问

pytest Fixture中使用yield而非return的场景(针对patch)

已理解的yield用法:测试后清理

我明白当测试需要执行后续清理操作时,pytest Fixture里使用yield的逻辑:测试完成后会回到Fixture执行yield语句后的代码,实现资源的清理。比如创建测试用户后自动删除的场景:

@pytest.fixture
def sending_user(mail_admin):
    user = mail_admin.create_user()
    yield user
    mail_admin.delete_user(user)

我的疑问:使用patch时为何不用return?

但我有个困惑:当Fixture仅需返回一个对象(比如unittest.mock.patch的实例),且看起来不需要额外手动清理时,为什么还是要用yield而非return?比如下面的示例代码:

from unittest.mock import patch, mock_open
from pytest import raises, fixture
import os.path

def read_from_file(file_path):
    if not os.path.exists(file_path):
        raise Exception("File does not exists!")
    with open(file_path, "r") as f:
        return f.read().splitlines()[0]

@fixture()
def mock_open_file():
    with patch("builtins.open", new_callable=mock_open, read_data="Correct string") as mocked:
        yield mocked # 为什么不用 return mocked

@fixture()
def mock_os():
    with patch("os.path.exists", return_value=True) as mocked:
        yield mocked # 为什么不用 return mocked

def test_read_file_and_returns_the_correct_string_with_one_line(mock_os, mock_open_file):
    result = read_from_file("xyz")
    mock_open_file.assert_called_once_with("xyz", "r")
    assert result == "Correct string"

def test_throws_exception_when_file_doesnt_exist(mock_os, mock_open_file):
    mock_os.return_value = False
    with raises(Exception):
        read_from_file("xyz")

原因解析

核心在于**with patch(...)上下文管理器的生命周期**:

  • 如果在with块里用return mocked,函数会直接返回,此时with块的退出逻辑(自动撤销patch)会立刻执行——这就导致测试用例运行时,patch已经被取消,mock完全起不到作用。
  • 而用yield mocked时,Fixture会暂停在yield处,把mock对象传递给测试用例;等测试用例执行完毕后,Fixture才会继续执行,此时with块才结束,触发撤销patch的逻辑。

这样就能保证:测试运行期间patch生效,测试结束后自动恢复原环境,完美对齐测试用例的生命周期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 01:13:16