使用unittest和mock测试调用urlopen的UUT时的问题求助
问题分析与解决方案
你当前的核心问题在于:使用new=Urlopen替换urllib.request.urlopen时,mock_urlopen指向的是Urlopen类本身,而非每次调用生成的实例。每次调用urlopen()都会创建新的Urlopen实例,导致类的call_count、call_args等属性无法正确统计实际调用情况。
要同时实现异常/返回码测试和调用行为校验,无需自定义Mock子类,直接利用unittest.mock的原生功能即可解决。
修改后的测试代码示例
test_a_mock.py
from uut import Call_URL import unittest import unittest.mock as mock from http import HTTPStatus from urllib.error import HTTPError, URLError class Test_webhook(unittest.TestCase): def test_webhook_bad_request_response(self): # 构建模拟响应对象,自定义返回码和响应内容 mock_response = mock.Mock() mock_response.getcode.return_value = HTTPStatus.BAD_REQUEST mock_response.read.return_value = '{"ok": "Mock data"}' # Patch urlopen,指定返回模拟响应 with mock.patch('urllib.request.urlopen', return_value=mock_response) as mock_urlopen: Call_URL("http://10.0.10.180:3001/api/push/test") # 校验调用次数与参数 self.assertEqual(mock_urlopen.call_count, 1) mock_urlopen.assert_called_once_with("http://10.0.10.180:3001/api/push/test") # 若需验证UUT的打印输出,可额外patch builtins.print捕获内容 def test_webhook_http_error_handling(self): # 构建模拟HTTPError异常 mock_http_error = HTTPError( url="http://example.com/test", code=HTTPStatus.NOT_FOUND, msg="Resource Not Found", hdrs=None, fp=None ) with mock.patch('urllib.request.urlopen', side_effect=mock_http_error) as mock_urlopen: Call_URL("http://example.com/test") self.assertEqual(mock_urlopen.call_count, 1) # 此处可添加对异常处理逻辑的验证(如捕获打印信息) def test_webhook_url_error_handling(self): # 构建模拟URLError异常 mock_url_error = URLError(reason="Connection Refused") with mock.patch('urllib.request.urlopen', side_effect=mock_url_error) as mock_urlopen: Call_URL("http://example.com/test") self.assertEqual(mock_urlopen.call_count, 1) if __name__ == '__main__': unittest.main()
关键优化点说明
- 放弃自定义Mock类:直接用
mock.Mock()构建响应对象,通过return_value指定urlopen的返回结果,避免实例化导致的统计失效问题。 - 灵活控制行为:
- 用
return_value模拟正常响应,可自定义getcode()、read()的返回值来测试不同状态码和响应内容。 - 用
side_effect抛出HTTPError/URLError,测试UUT的异常处理逻辑。
- 用
- 调用统计正常工作:此时
mock_urlopen是跟踪调用的Mock实例,call_count、call_args、assert_called_once_with()等方法均可正常使用。
扩展:多场景调用测试
如果需要测试多次调用返回不同结果的场景,可通过side_effect传入序列:
def test_multiple_different_responses(self): # 构建两个不同的模拟响应 ok_response = mock.Mock() ok_response.getcode.return_value = HTTPStatus.OK ok_response.read.return_value = '{"ok": true}' error_response = mock.Mock() error_response.getcode.return_value = HTTPStatus.BAD_GATEWAY error_response.read.return_value = '{"ok": false, "msg": "Server Error"}' with mock.patch('urllib.request.urlopen', side_effect=[ok_response, error_response]) as mock_urlopen: Call_URL("url1") Call_URL("url2") self.assertEqual(mock_urlopen.call_count, 2) self.assertEqual(mock_urlopen.call_args_list, [ mock.call("url1"), mock.call("url2") ])
内容的提问来源于stack exchange,提问作者Arthur Mitchell
相关产品推荐
相关产品推荐

