unittest mock replace/reset 修补对象时的模拟函数
unittest mock replace/reset mocked function when patching an object
我需要使用 unittest.mock.patch.object
模拟有时可能会失败的外部方法。在测试中,该方法将引发一些错误,然后 return 到原始行为。请注意,我要重现的行为比 return 'bar'
复杂得多,所以我不能只复制 Bar.some_method_that_may_fail
:
中的代码
import unittest
from unittest.mock import patch
class Bar(object):
def some_method_that_may_fail(self):
return "bar"
class Foo(object):
bar = None
def retry_method(self):
try:
self.__class__.bar = Bar().some_method_that_may_fail()
except KeyError:
self.retry_method()
class TestRetry(unittest.TestCase):
def setUp(self):
self.instance = Foo()
def test_retry(self):
# raise KeyError the first 5 calls to the function and then work normally
errors_list = [KeyError("")] * 5
def raise_errors(*_):
if errors_list:
errors_list.pop(0)
# TODO: return to original behaviour
with patch.object(Bar, 'some_method_that_may_fail', new=raise_errors) as mocked:
self.instance.retry_method()
self.assertEqual(self.instance.bar, 'bar')
if __name__ == '__main__':
unittest.main()
要return 后续调用的不同值,您可以使用side_effect。传递值数组 and/or 异常将 return 这些 values/raise 这些异常在后续调用中,在您的情况下是异常实例和原始调用的结果(如果这是您需要的) .
所以你的测试看起来像这样:
class TestRetry(unittest.TestCase):
def setUp(self):
self.instance = Foo()
def test_retry(self):
original = Bar.some_method_that_may_fail # save the original
with patch(__name__ + '.Bar') as mocked:
bar = mocked.return_value
side_effect = ([KeyError()] * 5) + [original(bar)]
bar.some_method_that_may_fail.side_effect = side_effect
self.instance.retry_method()
self.assertEqual(6, mocked.call_count)
self.assertEqual('bar', self.instance.bar)
一些注意事项:
- 我把
mock.patch.object
换成了mock.patch
,因为你没有要patch的对象(Bar
是在被测函数内部实例化的,需要patch实例,不是 class)
- 使用
__name__ + '.Bar'
进行修补是因为被测试的函数与测试函数在同一个模块中 - 在实际代码中,必须将其替换为正确的模块路径
- 我添加了一个检查
call_count
以确保该方法确实被调用了 6 次
另一件事:您的 Foo
中有错误,可能是因为您将其简化为示例。 foo
是一个 class 变量,但您正在设置一个同名的实例变量。您需要:
class Foo:
bar = None
def retry_method(self):
try:
self.__class__.bar = Bar().some_method_that_may_fail()
except KeyError:
self.retry_method()
(注意 self.__class__.bar
)
我需要使用 unittest.mock.patch.object
模拟有时可能会失败的外部方法。在测试中,该方法将引发一些错误,然后 return 到原始行为。请注意,我要重现的行为比 return 'bar'
复杂得多,所以我不能只复制 Bar.some_method_that_may_fail
:
import unittest
from unittest.mock import patch
class Bar(object):
def some_method_that_may_fail(self):
return "bar"
class Foo(object):
bar = None
def retry_method(self):
try:
self.__class__.bar = Bar().some_method_that_may_fail()
except KeyError:
self.retry_method()
class TestRetry(unittest.TestCase):
def setUp(self):
self.instance = Foo()
def test_retry(self):
# raise KeyError the first 5 calls to the function and then work normally
errors_list = [KeyError("")] * 5
def raise_errors(*_):
if errors_list:
errors_list.pop(0)
# TODO: return to original behaviour
with patch.object(Bar, 'some_method_that_may_fail', new=raise_errors) as mocked:
self.instance.retry_method()
self.assertEqual(self.instance.bar, 'bar')
if __name__ == '__main__':
unittest.main()
要return 后续调用的不同值,您可以使用side_effect。传递值数组 and/or 异常将 return 这些 values/raise 这些异常在后续调用中,在您的情况下是异常实例和原始调用的结果(如果这是您需要的) . 所以你的测试看起来像这样:
class TestRetry(unittest.TestCase):
def setUp(self):
self.instance = Foo()
def test_retry(self):
original = Bar.some_method_that_may_fail # save the original
with patch(__name__ + '.Bar') as mocked:
bar = mocked.return_value
side_effect = ([KeyError()] * 5) + [original(bar)]
bar.some_method_that_may_fail.side_effect = side_effect
self.instance.retry_method()
self.assertEqual(6, mocked.call_count)
self.assertEqual('bar', self.instance.bar)
一些注意事项:
- 我把
mock.patch.object
换成了mock.patch
,因为你没有要patch的对象(Bar
是在被测函数内部实例化的,需要patch实例,不是 class) - 使用
__name__ + '.Bar'
进行修补是因为被测试的函数与测试函数在同一个模块中 - 在实际代码中,必须将其替换为正确的模块路径 - 我添加了一个检查
call_count
以确保该方法确实被调用了 6 次
另一件事:您的 Foo
中有错误,可能是因为您将其简化为示例。 foo
是一个 class 变量,但您正在设置一个同名的实例变量。您需要:
class Foo:
bar = None
def retry_method(self):
try:
self.__class__.bar = Bar().some_method_that_may_fail()
except KeyError:
self.retry_method()
(注意 self.__class__.bar
)