我有一个名为test_module
import requests
def send_msg(msg, retries=0):
try:
# send the message here, e.g. a http request
response = requests.get("http://www.doesnotexist98734.com")
# if url does not exist raise an exception
except Exception as e:
if retries == 0:
raise e
else:
return send_msg(msg, retries=retries-1)
我的问题是如何在设置retries = n时编写一个单元测试来检查send_msg
函数被调用n次。我正在玩模拟模块(我使用python 2.7),我想我想要这样的东西,
import mock, unittest
class MyUnitTest(unittest.TestCase):
@mock.patch('test_module.send_msg')
def test_send_msg_tries_n_times(self, mock_send_msg):
with self.assertRaises(Exception):
mock_send_msg("hello", retries=3)
self.assertEqual(mock_send_msg.call_count, 4) # initial call + 3 retries
然而,由于我嘲笑了它不会调用真实函数的函数,所以我没有得到异常,也没有递归调用自己......
答案 0 :(得分:3)
您无法模拟测试中的功能。您希望测试预期结果,而不是函数正确使用递归。
模拟request.get()
调用,并让它始终产生异常。然后计算你的模拟被调用的频率。
@mock.patch('requests.get')
def test_send_msg_tries_n_times(self, req_get_mock):
req_get_mock.side_effect = Exception
with self.assertRaises(Exception):
send_msg("hello", retries=3)
self.assertEqual(req_get_mock.call_count, 4) # 1 initial call + 3 retries
如果您将来想要避免使用递归并希望使用迭代,那么您的测试仍然可以正常运行,因为它验证了行为,而不是特定的实现。您可以安全地重构被测功能。
答案 1 :(得分:1)
我发现验证函数的递归调用结构是单元测试的一个非常有用的功能。
这可以通过使用side_effect
中的Mock
参数轻松完成,而不是使用patch
装饰器。
side_effect
接受一个函数,该函数将传递与Mock
相同的参数。 Mock
的返回值也是side_effect
函数的返回值。这意味着我们可以将原始函数作为Mock
传递给side_effect
,并在我们的递归函数周围有效地使用Mock
包装。
例如:
def test_send_msg_tries_n_times(self, mock_send_msg):
test_module.send_msg = Mock(side_effect=test_module.send_msg)
test_module.send_msg("hello", retries=3)
test.module.send_msg.assert_has_calls([
call("hello", retries=3),
call("hello", retries=2),
call("hello", retries=1),
call("hello", retries=0),
])