如何mock类中带self参数的@property属性方法?
针对特定实例mock属性的两种解决方案
1. Mock @property 装饰的属性
@property本质是描述符,它的getter逻辑存放在fget属性中,直接patch这个fget方法即可拿到self参数,实现按实例条件返回自定义值:
首先定义补丁逻辑:
from unittest.mock import patch from functools import partial # 保留原property的getter逻辑,避免其他实例受影响 orig_conf_getter = Thing.configuration_filename.fget def patched_conf_getter(custom_path, self): # 按实例属性判断是否要返回自定义路径 if self.name == 'test_thing': return custom_path # 其他实例走原逻辑 return orig_conf_getter(self)
测试类的装饰器写法和你原有逻辑完全兼容,仅需要修改patch的目标:
@patch.object(Thing.configuration_filename, 'fget', side_effect=partial(patched_conf_getter, 'testdata/test_1.yaml')) class ConfigurationTests(TestCase): def test_sample(self, mocked_getter): # 匹配条件的实例返回自定义路径 test_inst = Thing('test_thing') assert test_inst.configuration_filename == 'testdata/test_1.yaml' # 普通实例不受影响 normal_inst = Thing('normal') assert normal_inst.configuration_filename == '/configuration/normal_configuration.yaml'
2. Mock init 中初始化的实例属性
这种实例级别的属性有两种处理方式:
- 如果测试中可以直接拿到实例对象,直接赋值覆盖即可,最简单高效:
test_inst = Thing('test_thing') test_inst.configuration_name = 'testdata/test_1.yaml'
- 如果无法直接干预实例创建过程,可以patch
__init__方法,在初始化完成后按条件赋值:
# 保留原初始化逻辑 orig_init = Thing.__init__ def patched_init(custom_path, self, name, *args, **kwargs): # 先执行原初始化逻辑 orig_init(self, name, *args, **kwargs) # 匹配条件的实例覆盖属性 if self.name == 'test_thing': self.configuration_name = custom_path # 测试类装饰器写法 @patch.object(Thing, '__init__', side_effect=partial(patched_init, 'testdata/test_1.yaml'), autospec=True) class ConfigurationTests(TestCase): def test_sample(self, mocked_init): test_inst = Thing('test_thing') assert test_inst.configuration_name == 'testdata/test_1.yaml'
以上两种方案都不需要修改原有业务代码,不需要将@property改为普通方法,完全满足仅针对特定实例mock的需求。
内容的提问来源于stack exchange,提问作者fatz
相关产品推荐
相关产品推荐

