Python unittest中PropertyMock的side_effect仅单次调用问题
问题根因
test_prop输出两次text_0的核心原因是patch参数写法错误:
new_callable参数要求传入可调用的类/工厂函数,由patch在进入上下文管理器时自动调用它,生成用来替换目标属性的mock实例。- 你当前写法是提前实例化好
PropertyMock(side_effect=side_effect_func)对象直接传给new_callable。patch检测到传入的不是可调用的工厂,而是已经生成的实例对象时,会直接把该实例作为静态替换值,不会正确触发PropertyMock作为描述符的每次访问拦截逻辑:第一次属性访问拿到side_effect返回值后,后续访问会直接复用第一次的结果,不会重复执行side_effect函数。
test_func能正常输出text_0, text_1,是因为你直接把side_effect参数传给了patch,由patch正确给替换方法的Mock实例配置side_effect,每次调用方法都会正常触发计数累加。
正确实现方式
要让property每次被访问都触发side_effect,按以下两点修正即可:
- 给
new_callable传入PropertyMock类本身,不要提前实例化;将side_effect作为patch.object/patch的独立关键字参数传入,patch会自动把这些参数透传给PropertyMock的构造函数,生成正确配置的mock实例。 - side_effect函数需要预留一个形参,接收触发属性访问的类实例——PropertyMock执行side_effect时,会自动把当前访问属性的实例作为第一个参数传入。
修正后的test_prop代码如下:
def test_prop(self): side_effect_counter = -1 # 增加形参接收PropertyMock自动传入的实例对象 def side_effect_func(instance): nonlocal side_effect_counter side_effect_counter += 1 return f'text_{side_effect_counter}' c2_1 = dut.Class2('class2', dut.Class1('class1')) c2_2 = dut.Class2('class2_2', dut.Class1('class1_2')) # new_callable传PropertyMock类,side_effect作为独立参数传入 with patch.object(dut.Class1, 'name', new_callable=PropertyMock, side_effect=side_effect_func): print(f'{c2_2.class1.name}, {c2_1.class1.name}')
执行后输出和test_func一致,为text_0, text_1,每次访问property都会正常触发side_effect函数,计数按访问次数累加。
补充说明:如果需要根据不同实例返回不同值,直接在side_effect_func里判断传入的
instance参数即可,不需要依赖全局计数器,更贴合模拟不同Class1实例属性的场景需求。
内容的提问来源于stack exchange,提问作者user2606240
相关产品推荐
相关产品推荐

