You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python 3.10中mock.patch.object的side_effect与wraps有何区别?

Python 3.10中mock.patch.object的side_effect与wraps区别及测试对比

一、side_effect与wraps的核心差异

1. 执行逻辑本质

  • side_effect:将传入的可调用对象作为mock调用时的执行主体,每次调用mock都会直接触发这个对象,mock自身的return_value会被完全忽略,最终返回值由side_effect的执行结果决定。如果传入原方法,就相当于把mock变成原方法的调用转发器,但仍保留mock的调用记录等监控特性。
  • wraps:创建一个代理mock,它会先完成mock自身的职责(比如记录调用次数、参数),再去调用被包裹的原对象/方法,最终返回原方法的执行结果。mock本身是原对象的一层包装,而非单纯的调用转发。

2. 功能灵活性

  • side_effect支持更多场景:除了传入可调用对象,还可以传入异常实例(调用时直接抛出)、迭代器(每次调用依次返回迭代器中的值),甚至是None(恢复mock默认的return_value行为)。
  • wraps的核心是「保留原对象行为」,它更专注于在不改变原逻辑的前提下,为原对象添加mock的监控能力,功能相对单一,主要用于代理原对象的调用和属性访问(如果包裹的是实例对象)。

3. 属性与调用的处理

  • 如果wraps的是一个实例对象(而非单个方法),访问mock的属性会直接转发给原实例;而side_effect仅处理mock的调用行为,mock的属性都是自身的,与原对象无关。
  • 对于return_value的优先级:side_effect的优先级高于return_value,只要设置了有效的side_effect,return_value就会失效;而wraps下,return_value仅在直接访问mock.return_value时生效,调用mock时仍会执行原方法。

二、两个单元测试的差异分析

你给出的两个测试用例表面上结果一致,但底层执行逻辑有明显区别:

test_side_effect的执行流程

当调用spud.foo(n=40)时,实际触发的是mock的side_effect——也就是原spud.foo方法,mock仅负责记录这次调用的参数和次数,返回值直接取自原方法的执行结果。此时mock更像一个「调用记录器」,实际执行逻辑完全由side_effect指定的原方法接管。

test_wraps的执行流程

当调用spud.foo(n=40)时,首先是mock实例接收调用请求,记录调用的参数和次数,然后mock再去调用被包裹的原spud.foo方法,将原方法的返回值作为自己的返回值返回。此时mock是原方法的「代理层」,所有调用都会先经过mock的处理,再转发给原方法。

可观测的差异场景

如果对两个测试做以下修改,就能看到明显区别:

  1. 修改mock的return_value:
    • 在test_side_effect中添加mock_foo.return_value = 99,最终结果还是42,因为side_effect的返回值优先级更高。
    • 在test_wraps中添加同样的代码,结果依然是42,因为调用mock时会优先执行被包裹的原方法,return_value仅在直接访问mock_foo.return_value时才会返回99。
  2. 替换side_effect:
    • 在test_wraps中添加mock_foo.side_effect = lambda n: n + 10,此时mock会忽略原方法,直接执行这个lambda,返回结果变为50;而test_side_effect中如果替换side_effect,效果类似,但本质是直接替换了执行体,而非覆盖代理逻辑。

内容的提问来源于stack exchange,提问作者Marcel Wilson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 18:24:29