pytest:如何在单元测试期间强制引发异常?

jef*_*tle 5 python unit-testing exception pytest python-requests

在我的python代码中,我期望在调用method之后可能引发异常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
Run Code Online (Sandbox Code Playgroud)

小智 8

在我的应用程序中,我捕获异常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
Run Code Online (Sandbox Code Playgroud)


Enr*_*aez 5

您可以使用 py.test raises,请在此处查看: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())
Run Code Online (Sandbox Code Playgroud)

此外,您还可以使用 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())
Run Code Online (Sandbox Code Playgroud)

在这里你可以找到更多关于参数化的例子:http : //layer0.authentise.com/pytest-and-parametrization.html