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

如何Mock attr.ib()的默认值以避免单元测试抛出异常?

解决 attrs 类中 init=False 属性默认值的 Mock 问题

这个问题我之前也踩过坑,核心原因很明确:你定义_prop时用了default=OtherClass(),这行代码是在Python加载ExampleClass类的阶段就执行了——也就是测试运行前,真实的OtherClass实例已经被创建并作为默认值绑定到属性上了。而你测试里的mocker.patch是在测试运行时才生效的,自然没法影响这个早早就存在的真实实例。

下面给你两个可行的解决方案,优先推荐第一个:

方案1:用工厂函数延迟实例化(最稳妥)

attrs提供了factory参数来替代直接赋值的默认值,它会在每次创建ExampleClass实例的时候才调用传入的函数/类来生成默认值。修改你的ExampleClass定义:

@attrs(auto_attribs=True)
class ExampleClass:
    # 用 factory 替代直接的 OtherClass(),延迟实例化时机
    _prop: OtherClass = attrib(init=False, factory=OtherClass)

这样调整后,当你在测试里patchOtherClass之后,创建sut = ExampleClass()时,factory=OtherClass会调用被patch后的Mock类,生成一个MagicMock实例赋值给self._prop。这时候调用self._prop.some_func()就会触发Mock的方法,不会再抛出NotImplementedException了。

方案2:在patch后覆盖类的默认值(应急方案,不推荐)

如果因为某些原因没法修改原类的代码,那可以在测试里patch之后,手动覆盖ExampleClass中_prop属性的默认值。attrs会把类的属性元信息存在__attrs_attrs__这个类属性里,我们可以找到对应属性修改它的默认值:

def test_example(mocker):
    mock_other_cls = mocker.patch("path.to.example_class.OtherClass")
    # 找到 _prop 属性的元信息,替换默认值为Mock实例
    for attr in ExampleClass.__attrs_attrs__:
        if attr.name == "_prop":
            attr.default = mock_other_cls()
            break
    sut = ExampleClass()
    sut.some_func()

这个方法能解决问题,但缺点是依赖attrs的内部实现(__attrs_attrs__),如果后续attrs版本更新改变了这个结构,测试就会失效,所以只适合应急用。

再啰嗦一句为什么直接patch没用

再明确一下时间线:

  1. Python加载ExampleClass类 → 执行OtherClass()创建真实实例 → 把这个实例作为_prop的默认值存在类里
  2. 测试运行 → 执行mocker.patch替换OtherClass为Mock
  3. 创建ExampleClass实例 → 用之前已经存在的真实实例作为_prop的默认值
    所以patch根本碰不到这个早早就创建好的真实实例,自然会抛异常。而用factory的话,步骤3才会调用OtherClass(),这时候已经是Mock类了,生成的就是Mock实例。

内容的提问来源于stack exchange,提问作者Cameron Hurst

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:59:42