[英]pytest: How to force raising Exceptions during unit-testing?
在我的python代碼中,我期望在調用方法requests.Session.request()
之后可能會引發異常,例如:
requests.exceptions.ConnectTimeout
requests.exceptions.ReadTimeout
requests.exceptions.Timeout
當引發任何這些預期的異常時,我會適當地處理它們,例如可能是重試情況。
我的問題是,我使用py.test
進行單元測試,我故意想從代碼的特定部分注入異常。 例如,調用requests.Session.request()
的函數,而不是返回有效的requests.Response
,它會引發requests.exception
。
我想確保我的代碼成功處理來自其他包的預期和意外異常,其中包括來自requests
異常。
也許......是否有一個@decorator
我可以添加到上述功能中,以便在單元測試期間根據請求引發異常?
為單元測試進行異常注射的建議? (對我的問題的恰當措辭將不勝感激。)
謝謝你的回復!
這是創建requests.Session
並調用requests.Session.request()
的整個單例類:
class MyRequest(metaclass=Singleton):
def __init__(self, retry_tries=3, retry_backoff=0.1, retry_codes=None):
self.session = requests.session()
if retry_codes is None:
retry_codes = set(REQUEST_RETRY_HTTP_STATUS_CODES)
self.session.mount(
'http',
HTTPAdapter(
max_retries=Retry(
total=retry_tries,
backoff_factor=retry_backoff,
status_forcelist=retry_codes,
),
),
)
def request(self, request_method, request_url, **kwargs):
try:
return self.session.request(method=request_method, url=request_url, **kwargs)
except Exception as ex:
log.warning(
"Session Request: Failed: {}".format(get_exception_message(ex)),
extra={
'request_method': request_method,
'request_url': request_url
}
)
raise
您可以使用py.test加注,請在此處查看: http ://doc.pytest.org/en/latest/assert.html#assertions-about-expected-exceptions
考慮到您的代碼,您可以執行以下操作:
from requests.exceptions import ConnectTimeout, ReadTimeout, Timeout
from unittest.mock import patch
import pytest
class TestRequestService:
@patch('path_to_module.MyRequest')
def test_custom_request(self, my_request_mock):
my_request_mock.request.side_effect = ConnectTimeout
with pytest.raises(ConnectTimeout):
my_request_mock.request(Mock(), Mock())
此外,您還可以使用pytest.parametrize( http://doc.pytest.org/en/latest/parametrize.html ):
from requests.exceptions import ConnectTimeout, ReadTimeout, Timeout
from unittest.mock import patch
import pytest
class TestRequestService:
@pytest.mark.parametrize("expected_exception", [ConnectTimeout, ReadTimeout, Timeout])
@patch('path_to_module.MyRequest')
def test_custom_request(self, my_request_mock, expected_exception):
my_request_mock.request.side_effect = expected_exception
with pytest.raises(expected_exception):
my_request_mock.request(Mock(), Mock())
在這里,您可以找到有關參數化的更多示例: http : //layer0.authentise.com/pytest-and-parametrization.html
在我的應用程序中,我捕獲異常requests.exceptions.ConnectionError
並返回下面的expected
變量中的消息。 所以測試看起來像這樣:
import pytest
import requests
expected = {'error': 'cant connect to given url'}
class MockConnectionError:
def __init__(self, *args, **kwargs):
raise requests.exceptions.ConnectionError
def test_project_method(monkeypatch):
monkeypatch.setattr("requests.get", MockConnectionError)
response = project_method('http://some.url.com/')
assert response == expected
修補,嘲弄和依賴注入是注入假物體的技術。 修補有時很難正確,另一方面,依賴注入要求必須更改要測試的代碼。
這只是一個如何使用依賴注入的簡單示例。 首先是我們要測試的代碼:
import requests
...
def fetch_data(url, get=requests.get):
return get(url).json()
# this is how we use fetch_data in productive code:
answer = fetch_data("www.google.com?" + term)
然后是測試:
import pytest
def test_fetch():
def get_with_timeout(url):
raise ConnectTimeout("message")
with pytest.raises(ConnectTimeout) as e:
# and now we inject the fake get method:
fetch_data("https://google.com", get=get_with_timeout)
assert e.value == "message"
在上面的示例中,模擬技術如下:
def test_exception():
class TimeoutSessionMock:
def get(self, *args, **kwargs):
raise ConnectTimeout("message")
mr = MyRequest()
mr.session = TimeoutSessionMock()
with pytest.raises(ConnectTimeout) as e:
mr.request("get", "http://google.com")
assert e.value == "message"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.