在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
相关产品推荐
相关产品推荐

