我想在具有默认 side_effect 的许多测试的类上使用 mock.patch 装饰器,然后在某些特定测试中将该 side_effect 替换为其他行为。当我尝试用异常替换 side_effect 时,模拟代码会引发
TypeError: 'Exception' object is not iterable
。
请注意,只有在装饰器中使用
autospec=True
时才会发生这种情况。 删除或替换为 spec=my_original_function
时,一切都会按预期工作。
这是 mock 模块上的错误吗?我正在使用版本“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): # This works
self.assertEqual(my_patch(), 'my_default_side_effect')
def test_default_side_effect_can_be_replaced_with_new_function(self, my_patch): # This works
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): # This works
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): # This fails
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"
我不确定为什么 autospec=true 会导致错误,但有一种方法可以解决这个问题。只需使用 raise_exception 代替或尝试修改自定义对象。这是一个例子:
from unittest import TestCase
from unittest.mock import patch, Mock
def my_original_function():
return 'my_original_function'
def my_default_side_effect():
return 'my_default_side_effect'
def raise_exception(*args, **kwargs):
raise Exception('exception_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 = raise_exception
with self.assertRaises(Exception) as cm:
my_patch()
self.assertEqual(str(cm.exception), 'exception_side_effect')