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

使用冻结异常时单元测试出现意外FrozenInstanceError的解决方法

问题:unittest中抛出带冻结异常上下文的BugException时框架崩溃

问题背景

编写单元测试展示Bug失败场景时,要求测试报错且错误信息清晰指向BugException,但当BugException在捕获被@attrs.frozen装饰的异常类实例后抛出时,unittest框架会崩溃,真实的BugException被隐藏,反而输出FrozenInstanceError。

最小复现代码

import unittest

import attrs


@attrs.frozen
class FrozenException(Exception):
    pass


class BugException(Exception):
    pass


class Test(unittest.TestCase):

    def testFromNone(self):
        raise BugException

    def testFromFrozen(self):
        try:
            raise FrozenException
        except FrozenException:
            raise BugException


if __name__ == '__main__':
    unittest.main()

异常现象

  • 执行testFromNone时符合预期,测试失败并明确显示BugException:
$ python3 -m unittest scratch.Test.testFromNone
E
======================================================================
ERROR: testFromNone (scratch.Test.testFromNone)
----------------------------------------------------------------------
Traceback (most recent call last):
...
scratch.BugException

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)
  • 执行testFromFrozen时,unittest框架直接崩溃,输出冗长堆栈跟踪,最终错误为attr.exceptions.FrozenInstanceError,真实的BugException被完全隐藏:
$ python3 -m unittest scratch.Test.testFromFrozen
Traceback (most recent call last):
...
<very long traceback>
...
attr.exceptions.FrozenInstanceError

原因分析

unittest框架会对测试未捕获的异常做后处理,过程中尝试修改异常的堆栈追踪信息。由于BugException的__context__属性指向了冻结的FrozenException实例,框架修改时触发了冻结实例不可修改的限制,抛出FrozenInstanceError。

解决方案

方案1:抛出BugException时清除异常上下文

在抛出BugException时使用raise ... from None语法,显式清除异常上下文,避免unittest访问冻结的实例:

def testFromFrozen(self):
    try:
        raise FrozenException
    except FrozenException:
        # 清除上下文,阻止unittest访问冻结的FrozenException实例
        raise BugException from None

方案2:允许修改冻结异常的__traceback__属性

通过attrs的field配置,单独允许修改异常类的__traceback__属性(unittest框架会尝试修改该属性),同时保留其他属性的冻结状态:

import attrs

@attrs.frozen
class FrozenException(Exception):
    # 允许修改__traceback__属性,适配unittest的后处理逻辑
    __traceback__ = attrs.field(init=False, default=None, on_setattr=attrs.setters.allow)

验证结果

采用任意一种方案后,执行testFromFrozen会正常失败,错误信息正确指向BugException:

$ python3 -m unittest scratch.Test.testFromFrozen
E
======================================================================
ERROR: testFromFrozen (scratch.Test.testFromFrozen)
----------------------------------------------------------------------
Traceback (most recent call last):
...
scratch.BugException

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 20:55:24