簡體   English   中英

pytest:如何在單元測試期間強制提​​高異常?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM