如何模拟引发urllib错误

时间:2019-11-19 16:01:29

标签: python-3.x mocking pytest urllib pytest-mock

阅读this in the python docs后,我在HTTPError中捕获了URLError(通过get_response_from_external_api的{ {1}}个电话)可以引发:

foo.main.py

make_request_and_get_response

在测试urllib方法时,我试图模拟引发urlopenfrom urllib.request import urlopen import contextlib from urllib.error import HTTPError, URLError def make_request_and_get_response(q): with contextlib.closing(urlopen(q)) as response: return response.read() def get_response_from_external_api(q): try: resp = make_request_and_get_response(q) return resp except URLError as e: print('Got a URLError: ', e) except HTTPError as e: print('Got a HTTPError: ', e) if __name__ == "__main__": query = 'test' result = get_response_from_external_api(query) print(result) 异常

foo.test_main.py

get_response_from_external_api

但是我得到一个HTTPError。我是python模拟语法的新手,正在寻找示例。

我已经阅读了this answer,但是在URLError(通过from foo.main import get_response_from_external_api import pytest from unittest.mock import patch, Mock from urllib.error import HTTPError, URLError def test_get_response_from_external_api_with_httperror(capsys): with patch('foo.main.make_request_and_get_response') as mocked_method:      with pytest.raises(HTTPError) as exc:             mocked_method.side_effect = HTTPError() # TypeError             resp = get_response_from_external_api(mocked_method)             out, err = capsys.readouterr()             assert resp is None             assert 'HTTPError' in out             assert str(exc) == HTTPError def test_get_response_from_external_api_with_urlerror(capsys): with patch('foo.main.make_request_and_get_response') as mocked_method:      with pytest.raises(URLError) as exc:             mocked_method.side_effect = URLError() # TypeError             resp = get_response_from_external_api(mocked_method)             out, err = capsys.readouterr()             assert resp is None             assert 'URLError' in out             assert str(exc) == URLError 的返回值超出了除外的范围之内的情况下,我看不到如何应用此方法。 -块。不知道我是否应该模拟整个TypeError: __init__() missing 5 required positional arguments: 'url', 'code', 'msg', 'hdrs', and 'fp'而不是here

1 个答案:

答案 0 :(得分:5)

无需模拟urlopen的各个部分-通过模拟函数引发异常,可以确保不会调用urlopen

由于您正在创建这些异常以检查您的错误处理代码是否正常运行,因此它们不需要完整-它们仅包含满足测试所需的最少信息。

HTTPError需要五个参数:

  • 一个网址
  • HTTP状态代码
  • 错误消息
  • 请求标头
  • 类似文件的对象(响应的主体)

出于模拟目的,这些都可以是None,但构造看起来像真正错误的对象可能会有所帮助。如果要读取“文件状对象”,则可以传递包含示例响应的io.BytesIO实例,但是根据问题中的代码,这似乎没有必要。

>>> h = HTTPError('http://example.com', 500, 'Internal Error', {}, None)
>>> h
<HTTPError 500: 'Internal Error'>

URLError需要单个参数,该参数可以是字符串或异常实例。出于模拟目的,字符串就足够了。

>>> u = URLError('Unknown host')
>>> u

URLError('Unknown host')

以下是该问题的代码,将其修改为考虑到上述内容。无需将模拟函数传递给自身-只需传递任意字符串即可。我删除了with pytest.raises块,因为该异常被捕获在您的代码的try / except块中:您正在测试自己的代码可以处理该异常本身,而不是该异常会渗透到测试函数中。

from foo.main import get_response_from_external_api

import pytest
from unittest.mock import patch, Mock
from urllib.error import HTTPError, URLError

def test_get_response_from_external_api_with_httperror(capsys):
    with patch('foo.main.make_request_and_get_response') as mocked_method:
        mocked_method.side_effect = HTTPError('http://example.com', 500, 'Internal Error', {}, None)
        resp = get_response_from_external_api('any string')
        assert resp is None
        out, err = capsys.readouterr()
        assert 'HTTPError' in out


def test_get_response_from_external_api_with_urlerror(capsys):
    with patch('foo.main.make_request_and_get_response') as mocked_method:
        mocked_method.side_effect = URLError('Unknown host')
        resp = get_response_from_external_api('any string')
        assert resp is None
        out, err = capsys.readouterr()
        assert 'URLError' in out

最后,您需要反转除块以外的尝试顺序-HTTPErrorURLError的子类,因此您需要先对其进行测试,否则它将由{{ 1}}块。