Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pytest: How to force raising Exceptions during unit-testing?

In my python code, I am expecting exceptions could possibly be raised after calling method requests.Session.request(), for example these:

  • requests.exceptions.ConnectTimeout
  • requests.exceptions.ReadTimeout
  • requests.exceptions.Timeout

When any of these expected exceptions are raised, I handle them appropriately, for example possibly a retry situation.

My question, I am using py.test for unit testing, and I purposely want to inject raising exceptions from specific parts of my code. For example, the function that calls requests.Session.request(), instead of returning a valid requests.Response, it raises a requests.exception.

What I want to make sure that my code successfully handles expected and unexpected exceptions coming from other packages, which include those exceptions from requests.

Maybe... Is there a @decorator that I could add to the aforementioned function to raise exceptions upon request during unit testing?

Suggestions for doing exceptions injections for unit testing? (proper phrasing of my question would be greatly appreciated.)

Thanks for the responses!!!

Here is the entire singleton class that creates requests.Session and calls 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
like image 608
jeff00seattle Avatar asked Dec 24 '16 16:12

jeff00seattle


1 Answers

You can make use of py.test raises, check it here: http://doc.pytest.org/en/latest/assert.html#assertions-about-expected-exceptions

Taking into account your code you could do something along the lines of the following:

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())

Moreover, you could make use of pytest.parametrize(http://doc.pytest.org/en/latest/parametrize.html) as well:

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())

Here you can find some more examples about parametrize: http://layer0.authentise.com/pytest-and-parametrization.html

like image 133
Enrique Saez Avatar answered Sep 27 '22 20:09

Enrique Saez