如何以上下文管理器方式使用patch()并获取自定义Mock实例
上下文返回的mock对象本质
当向patch()传入自定义类作为替换目标时,as关键字绑定的对象就是传入的DatabaseMock类本身,而非类的实例。patch的执行逻辑仅为将指定导入路径下的原对象替换为传入的新对象,不会主动触发类的实例化。类的实例化动作只会在被测试代码实际调用该数据库类时发生。你在__init__方法中定义的x是实例属性,仅在类实例化后才会绑定到实例对象上,直接访问类本身的x属性自然会抛出AttributeError。
将x改为类属性后可以访问,本质是因为类对象本身持有了该属性,但类属性会被所有类实例共享,不同测试用例运行时如果没有手动重置,状态会持续保留,必然违反测试隔离要求,该方案不可用。
正确获取实例的实现方式
以下三种方案均可以满足测试隔离要求,根据业务场景选择即可:
- 方案1:通过
return_value获取单实例
只要被测试代码中仅对数据库类做一次实例化,生成的实例会自动挂载到类的return_value属性上(patch会自动为传入的替换类添加mock跟踪属性),直接访问即可:
如果被测试代码中存在多次实例化逻辑,可通过from unittest.mock import patch class TestFunctions(): def test_function(self): with patch("path.to.database.call", DatabaseMock) as mock_db_cls: result = function_i_am_testing() # 获取被测试代码运行时生成的DatabaseMock实例 mock_db_instance = mock_db_cls.return_value assert mock_db_instance.x == resultmock_db_cls.return_value_list按实例化顺序获取所有生成的实例。 - 方案2:自定义实例跟踪逻辑,适配多实例场景
如果测试逻辑涉及多次数据库类初始化,可在自定义mock类中添加轻量的实例跟踪能力,同时配套重置方法保证测试隔离:class DatabaseMock(): _instances = [] def __init__(self, host=None): self.host = host self.x = {} # 初始化时将当前实例存入跟踪列表 self.__class__._instances.append(self) @classmethod def reset(cls): # 清空实例记录,避免跨用例状态污染 cls._instances.clear() # 测试代码 class TestFunctions(): def setup_method(self): # 每个测试用例执行前先重置mock状态 DatabaseMock.reset() def test_function(self): with patch("path.to.database.call", DatabaseMock): result = function_i_am_testing() # 按实例化顺序获取对应实例即可 assert DatabaseMock._instances[0].x == result - 方案3:继承
MagicMock实现,保留原生mock能力
自定义__init__方法不影响类继承MagicMock,只要在构造方法中调用父类初始化逻辑,即可同时保留自定义属性和原生mock的所有能力,无需额外编写跟踪逻辑:
该写法完全兼容from unittest.mock import MagicMock class DatabaseMock(MagicMock): def __init__(self, host=None, *args, **kwargs): super().__init__(*args, **kwargs) self.host = host self.x = {}return_value、调用断言等原生mock特性,适配绝大多数测试场景。
注意:类级别属性仅可用于存储实例跟踪列表、重置方法这类测试辅助逻辑,禁止存储业务状态数据,每个用例执行前必须执行重置操作,从根源上避免跨用例状态污染。
内容的提问来源于stack exchange,提问作者dev
相关产品推荐
相关产品推荐

