假设我想测试我的通话
Greeter
,这取决于第3方类Foo
,而第3方类又取决于另一个类Bar
。我需要模拟 Foo
,但是如何设置和验证链式调用 self.foo.get_bar().format(name)
?
greeter.py:
class Bar:
def format(self, name):
return name.upper()
class Foo:
def __init__(self):
self.bar = Bar()
def get_bar():
return self.bar
class Greeter:
def __init__(self, foo):
self.foo = foo
def hi(self, name):
return f'Hi {self.foo.get_bar().format(name)}'
PS:这个问题纯粹是关于Python mock的使用,而不是关于编写Python代码和测试的最佳实践,所以我不会重构代码。
经过一番研究,我找到了以下解决方案:
from unittest import mock
from unittest.mock import patch
import greeter
import unittest
class TestGreeter(unittest.TestCase):
def setUp(self):
self.foo_patcher = patch('greeter.Foo')
self.foo_mock_class = self.foo_patcher.start()
self.foo_mock = self.foo_mock_class()
self.foo_mock.get_bar.return_value.format.return_value = 'EMY'
def tearDown(self):
self.foo_patcher.stop()
def test_greeter(self):
g = greeter.Greeter(self.foo_mock)
result = g.hi('emy')
self.assertEqual(result, 'Hi EMY')
self.foo_mock.get_bar.return_value.format.assert_called_once_with('emy')
self.foo_mock.get_bar.return_value.assert_has_calls([mock.call.format('emy')])
self.foo_mock.assert_has_calls([mock.call.get_bar(), mock.call.get_bar().format('emy')])
if __name__ == '__main__':
unittest.main()