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
How to Convert a Pil Image into a Numpy Array
Can You Add New Statements to Python's Syntax
Creating Dataframe from a Dictionary Where Entries Have Different Lengths
Where Is Python's Sys.Path Initialized From
Printing All Instances of a Class
How to Fix Overlapping Annotations/Text
Slicing a List in Python Without Generating a Copy
Understanding Nested List Comprehension
How to Get the Original Variable Name of Variable Passed to a Function
Python: Execute Cat Subprocess in Parallel
Why Don't These List Operations Return the Resulting List
Getting the Index of the Returned Max or Min Item Using Max()/Min() on a List
Update Value of a Nested Dictionary of Varying Depth