Note: This question is based on a previous question I asked but modified to be different according to this answer.
Using side_effect, I am trying to raise a 'URLError'
exception when a mock is called but I get a DID NOT RAISE
error that I do not understand.
I have a Query
class with a class method make_request_and_get_response
which can raise several exceptions. I am not catching the 'URLError'
exception within the get_response_from_external_api
method in main.py
, so I should be able to expect to be raised and, subsequently, its exception mocked.
query.py
from urllib.request import urlopen
import contextlib
import urllib
class Query:
def __init__(self, a, b):
self.a = a
self.b = b
self.query = self.make_query()
def make_query(self):
# create query request using self.a and self.b
return query
def make_request_and_get_response(self): # <--- the 'dangerous' method that can raise exceptions
with contextlib.closing(urlopen(self.query)) as response:
return response.read().decode('utf-8')
main.py
from foo.query import *
def get_response_from_external_api(query):
try:
response = query.make_request_and_get_response()
except urllib.error.HTTPError as e:
print('Got a HTTPError: ', e)
except Exception:
print('Got a generic Exception!')
# handle this exception
if __name__ == "__main__":
query = Query('input A', 'input B')
result = get_response_from_external_api(query)
return result
Using pytest
, I am trying to mock that 'dangerous' method (make_request_and_get_response
) with a side effect for a specific exception. Then, I proceed with creating a mocked Query
object to use when calling the make_request_and_get_response
with the expectation that this last call give a 'URLError' exception.
test_main.py
import pytest
from unittest.mock import patch
from foo.query import Query
from foo.main import get_response_from_external_api
class TestExternalApiCall:
@patch('foo.query.Query')
def test_url_error(self, mockedQuery):
with patch('foo.query.Query.make_request_and_get_response', side_effect=Exception('URLError')):
with pytest.raises(Exception) as excinfo:
q= mockedQuery()
foo.main.get_response_from_external_api(q)
assert excinfo.value = 'URLError'
# assert excinfo.value.message == 'URLError' # this gives object has no attribute 'message'
The test above gives the following error:
> foo.main.get_response_from_external_api(q)
E Failed: DID NOT RAISE <class 'Exception'> id='72517784'>") == 'URLError'
The same error occurs even if I catch the 'URLError'
exception and then re-raise it in get_response_from_external_api
.
Can someone help me understand what I am missing in order to be able to raise the exception in a pytest?
Update according to @SimeonVisser's response:
If I modify main.py
to remove the except Excpetion
case:
def get_response_from_external_api(query):
try:
response = query.make_request_and_get_response()
except urllib.error.URLError as e:
print('Got a URLError: ', e)
except urllib.error.HTTPError as e:
print('Got a HTTPError: ', e)
then the test in test_main.py
:
def test_url_error2(self):
mock_query = Mock()
mock_query.make_request_and_get_response.side_effect = Exception('URLError')
with pytest.raises(Exception) as excinfo:
get_response_from_external_api(mock_query)
assert str(excinfo.value) == 'URLError'
The test passes OK.