You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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()

关键优化点说明

  1. 放弃自定义Mock类:直接用mock.Mock()构建响应对象,通过return_value指定urlopen的返回结果,避免实例化导致的统计失效问题。
  2. 灵活控制行为:
    • 用return_value模拟正常响应,可自定义getcode()、read()的返回值来测试不同状态码和响应内容。
    • 用side_effect抛出HTTPError/URLError,测试UUT的异常处理逻辑。
  3. 调用统计正常工作:此时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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 05:26:01