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

使用mock.patch(autospec=True)替换side_effect为异常时触发TypeError

问题

我想用mock.patch装饰器给包含多个测试的类设置默认side_effect,然后在部分测试里替换成其他行为。但当我尝试替换成Exception时,mock抛出了TypeError: 'Exception' object is not iterable。

注意:这个问题只在装饰器里用autospec=True时才会出现。移除这个参数或者换成spec=my_original_function后一切正常。这是mock模块的Bug吗?我用的版本是1.1.0。

复现代码:

from unittest import TestCase
from mock import patch


def my_original_function():
    return 'my_original_function'


def my_default_side_effect():
    return 'my_default_side_effect'


@patch('tests.my_test.my_original_function', side_effect=my_default_side_effect, autospec=True)
class DebugTest(TestCase):
    def test_default_side_effect_works(self, my_patch):  # 正常运行
        self.assertEqual(my_patch(), 'my_default_side_effect')

    def test_default_side_effect_can_be_replaced_with_new_function(self, my_patch):  # 正常运行
        my_patch.side_effect = lambda: 'some_other_side_effect'
        self.assertEqual(my_patch(), 'some_other_side_effect')

    def test_default_side_effect_can_be_replaced_with_new_list(self, my_patch):  # 正常运行
        my_patch.side_effect = ['other_side_effect_1', 'other_side_effect_2']
        self.assertEqual(my_patch(), 'other_side_effect_1')
        self.assertEqual(my_patch(), 'other_side_effect_2')

    def test_default_side_effect_can_be_replaced_with_new_exception(self, my_patch):  # 运行失败
        my_patch.side_effect = Exception('exception_side_effect')
        with self.assertRaisesRegex(Exception, 'exception_side_effect'):
            my_patch()
            # 报错:AssertionError: "exception_side_effect" does not match "'Exception' object is not iterable"
解决方案

这不是mock的Bug,是autospec=True下的特殊行为导致的:

当使用autospec=True时,生成的mock对象会严格遵循原函数的接口,同时在处理side_effect时,会优先判断赋值的对象是否为可迭代类型(函数/方法除外)。当你直接赋值一个Exception实例时,mock会把它当成可迭代对象尝试遍历,但Exception本身不可迭代,因此抛出TypeError。

修复方法有两种:

  • 将Exception实例放入列表中
    mock会依次返回列表中的元素,如果元素是Exception实例,就会抛出该异常:

    def test_default_side_effect_can_be_replaced_with_new_exception(self, my_patch):
        my_patch.side_effect = [Exception('exception_side_effect')]
        with self.assertRaisesRegex(Exception, 'exception_side_effect'):
            my_patch()
    
  • 用函数来抛出异常
    给side_effect赋值一个函数,函数内部主动抛出异常:

    def test_default_side_effect_can_be_replaced_with_new_exception(self, my_patch):
        def raise_my_exception():
            raise Exception('exception_side_effect')
        my_patch.side_effect = raise_my_exception
        with self.assertRaisesRegex(Exception, 'exception_side_effect'):
            my_patch()
    

另外,建议升级mock版本到2.0以上,或者直接使用Python 3.3+自带的unittest.mock模块,新版本中已经优化了side_effect对Exception实例的处理逻辑,无需额外处理即可直接赋值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 20:03:27