How to Test That a Python Function Throws an Exception

How do you test that a Python function throws an exception?

Use TestCase.assertRaises (or TestCase.failUnlessRaises) from the unittest module, for example:

import mymod

class MyTestCase(unittest.TestCase):
def test1(self):
self.assertRaises(SomeCoolException, mymod.myfunc)

Unit Test exception block of simple python function

You could patch the Constants class and delete the attribute you access from the mock.

from unittest import mock

# replace __main__ with the package Constants is from
with mock.patch("__main__.Constants") as mock_constants:
del mock_constants.DELETE_FILES_ON_SUCCESS
option_check()

When option_check() tries to access Constants.DELETE_FILES_ON_SUCCESS, it will raise an AttributeError, allowing you to reach the except block.

How do you test that a function throws an exception?

The exception is not bubbled up (i.e. it is suppressed) because you caught it and then you did not re-raise it. In turn, your unit tests will fail because you are testing that your function raises that exception while it is suppressing it.

You either change the function or the unit test of the function depending on which behavior is desired.

Assuming the tests are right then your function need to re-raise the exception like this:

def get_foo():
try:
return requests.get("http://www.bongani.com")
except requests.exceptions.ConnectionError:
print("Error")
raise # <---------------- add this line

Another scenario, the function is right and the tests are wrong. You fix your tests to look like this:

class MyTestCase(unittest.TestCase):
@mock.patch('requests.get')
def test_foo(self, mock_requests_get):
mock_requests_get.side_effect = requests.exceptions.ConnectionError()
self.assertIsNone(get_foo()) # <----- no more assertRaises

How do you get a function that throws an exception when run in a mocked test to only throw from the function you want to test?

Not sure if this is the "pythonic" way or even the correct way but I came to a solution.

    @patch("add_entry_to_calendar.build")
def test_add_entry_to_calendar_400(self, mock_build):
self.e = None

#google's mock https://googleapis.github.io/google-api-python-client/docs/mocks.html
http = HttpMock('tests/config-test.json', {'status' : '400'})

service = build("calendar", "v3", http=http)
mock_service = mock_build("calendar", "v3", http=http, side_effect=IndexError)

try:
service.events().insert(calendarId='primary').execute(http=http)
except Exception as e:
self.e = e

mock_service.events.return_value.insert.return_value.execute.side_effect = self.e

self.assertEqual(add_entry_to_calendar({"A":"B"}), None)

How can I test for Exception cases in FastAPI with Pytest?

Following the discussion below the question, I assembled a working example for you. Typically, if you can't logically hit your except block, you can ensure that the try block is raising an Exception by monkey patching the function that is tried, and replace it with something that definitely will raise an exception. In the below example, I will change the function do_something() that is defined in app.py with replace_do_something() that will just raise an Exception when called.

You can put the following files in the same folder (not a module) and try it for yourself:

File app.py:

from fastapi import FastAPI, HTTPException
from fastapi.testclient import TestClient
import pytest

app = FastAPI()

def do_something():
return "world"

@app.get("/myroute")
async def myroute():
try:
text = do_something()
return {"hello": text}
except Exception:
raise HTTPException(400, "something went wrong")

File test_app.py:

import pytest
from fastapi.testclient import TestClient

from app import app

client = TestClient(app)

def replace_do_something():
raise Exception()
return

def test_read_main(monkeypatch: pytest.MonkeyPatch):
response = client.get("/myroute")
assert response.status_code == 200
assert response.json() == {"hello": "world"}

def test_read_main_with_error(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr("app.do_something", replace_do_something)
# Here we replace any reference to do_something
# with replace_do_something. Note the 'app.' prefix!

response = client.get("/myroute")
assert response.status_code == 400
assert response.json() == {"detail": "something went wrong"}

You can call the test_app.py file with pytest (I also have pytest-cov installed to demonstrate the 100% coverage):

(venv) jarro@MacBook-Pro-van-Jarro fastapi-github-issues % pytest --cov=app SO/pytestwithmock/test_app.py
===================================================== test session starts =====================================================
platform darwin -- Python 3.10.5, pytest-7.1.2, pluggy-1.0.0
rootdir: /Users/jarro/Development/fastapi-github-issues
plugins: anyio-3.6.1, cov-3.0.0
collected 2 items

SO/pytestwithmock/test_app.py .. [100%]

---------- coverage: platform darwin, python 3.10.5-final-0 ----------
Name Stmts Miss Cover
----------------------------------------------
SO/pytestwithmock/app.py 13 0 100%
----------------------------------------------
TOTAL 13 0 100%

====================================================== 2 passed in 0.19s ======================================================


Related Topics



Leave a reply



Submit