Как имитировать ошибки повышения urllib
Прочитав это в документах python, я улавливаюHTTPError
а также URLError
исключения в get_response_from_external_api
что make_request_and_get_response
(через urllib
с urlopen
колл) может поднять:
foo.main.py
from 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)
При тестировании get_response_from_external_api
метод, я пытаюсь издеваться над повышениемHTTPError
а также URLError
исключения:
foo.test_main.py
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'
. Я новичок в синтаксисе имитаций Python и ищу примеры.
Я прочитал этот ответ, но не вижу, как это можно применить в моем случае, когда возвращаемое значениеurllib.urlopen
(через get_response_from_external_api
) выходит за рамки блока except. Не уверен, стоит ли вместо этого издеваться над всемurllib.urlopen.read
а не как здесь?
1 ответ
Нет необходимости издеваться над частями urlopen
- высмеивая вашу функцию для создания исключения, вы гарантируете, что urlopen
не позвонят.
Поскольку вы создаете эти исключения, чтобы проверить, работает ли ваш код обработки ошибок, они не должны быть полными - они должны содержать только минимальную информацию, необходимую для удовлетворения ваших тестов.
HTTPError ожидает пять аргументов:
- URL
- код состояния 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
Наконец, вам нужно изменить порядок вашей попытки, кроме блоков - HTTPError
является подклассом URLError
, поэтому вам нужно сначала протестировать его, иначе он будет обработан except URLError
блок.