如何将多段mock.patch的with语句封装为可复用函数?
问题描述
编写单元测试时需要重复Mock大量对象,当前使用mock.patch的代码如下:
with mock.patch("project.folder.file.SomeClass.method") as mock_this, mock.patch("otherproj.directory.file.AnotherClass.some_method") as mock_that, ... 还有另外4个类似Mock ... : mock_this.return_value = True mock_that.side_effect = some_side_effect ... 更多配置 ...
希望封装成名为server_mock_boilerplate的函数,实现如下使用方式:
# 可能需要传入对象以处理全局变量Mock的情况 # 我可能只需要获取部分Mock对象 with server_mock_boilerplate(obj1, obj2) as (mock1, mock2, mock4): # 执行测试 ... # 测试中可能修改side_effect mock1.side_effect = something_else
困惑点:直接从with返回会关闭资源,不通过with激活的话mock.patch返回的不是MagicMock类型,不知道如何封装重复的with条目并获取指定Mock元组。
解决方案
用contextlib.contextmanager装饰器创建自定义上下文管理器,整合所有mock.patch操作,激活后配置默认Mock行为,最后yield出需要的Mock对象元组,退出时自动清理所有Patch。
实现代码
from contextlib import contextmanager import mock @contextmanager def server_mock_boilerplate(obj1=None, obj2=None): # 定义所有需要Patch的目标路径 patch_targets = [ "project.folder.file.SomeClass.method", "otherproj.directory.file.AnotherClass.some_method", # 补充另外4个需要Patch的路径 "third.target.module.Class.method", "fourth.target.module.function", "fifth.target.Class.attribute", "sixth.target.util.func" ] # 创建所有Patch对象 patches = [mock.patch(target) for target in patch_targets] # 激活所有Patch,获取对应的Mock实例 mocks = [] for patch in patches: mock_instance = patch.start() mocks.append(mock_instance) try: # 配置默认的Mock行为 mocks[0].return_value = True # 对应原代码中的mock_this mocks[1].side_effect = some_side_effect # 对应原代码中的mock_that # 补充其他Mock的默认配置 # 处理传入的全局变量对象(如果有需求) if obj1: # 示例:对obj1的某个属性做额外Mock或调整 # mock.patch.object(obj1, "attr", return_value="test").start() pass if obj2: pass # 返回需要的Mock元组,按需选择对应位置的Mock yield (mocks[0], mocks[1], mocks[3]) finally: # 确保所有Patch都停止,清理资源 for patch in patches: patch.stop()
使用说明
- 在with语句中可直接拿到指定的Mock对象,测试过程中能随时修改
side_effect、return_value等属性 - 若要调整返回的Mock集合,只需修改yield语句中的元组内容即可
- 传入的obj1、obj2可用于处理全局变量的特殊Mock场景,在try代码块中添加对应逻辑即可
内容的提问来源于stack exchange,提问作者Tomáš Zato
相关产品推荐
相关产品推荐

