Ryan Payne
Ryan Payne

Reputation: 6301

How do I assert that an HTTP exception was raised?

I'm making an HTTP request using the requests library. If the request fails, an exception is raised. For example (truncated and simplified):

main.py

from requests import get


def make_request():
    r = get("https://httpbin.org/get")
    r.raise_for_status()

I've written a test using pytest that mocks the request.

test_main.py

from unittest import mock
from unittest.mock import patch

import pytest
from requests import HTTPError

from main import make_request


@patch("main.get")
def test_main_exception(mock_get):
    exception = HTTPError(mock.Mock(status=404), "not found")
    mock_get(mock.ANY).raise_for_status.side_effect = exception

    make_request()

However, I'm getting the following error because the exception is raised in the test—causing the test to fail.

$ pytest
...
E               requests.exceptions.HTTPError: [Errno <Mock id='4352275232'>] not found

/usr/local/Cellar/python/3.7.2_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/mock.py:1011: HTTPError
==================================================== 1 failed in 0.52s ====================================================

How do I assert on the behavior that occurs when an HTTP exception is raised (e.g. 404 status code)?

Upvotes: 4

Views: 5854

Answers (2)

Anton Daneyko
Anton Daneyko

Reputation: 6503

Asking myself the same question I've found the answer in the FastAPI unit tests, see their GitHub repo test. Use raise_server_exceptions parameter. Pasting a relevant bit here:

@app.get("/server-error")
def route_with_server_error():
    raise RuntimeError("Oops!")


def test_override_server_error_exception_raises():
    with pytest.raises(RuntimeError):
        client.get("/server-error")


def test_override_server_error_exception_response():
    client = TestClient(app, raise_server_exceptions=False)
    response = client.get("/server-error")
    assert response.status_code == 500
    assert response.json() == {"exception": "server-error"}

Relevant bit from the Starlette docs:

By default the TestClient will raise any exceptions that occur in the application. Occasionally you might want to test the content of 500 error responses, rather than allowing client to raise the server exception. In this case you should use client = TestClient(app, raise_server_exceptions=False).

Upvotes: -1

Ryan Payne
Ryan Payne

Reputation: 6301

Use the pytest.raises context manager to capture the exception and assert on the error message. For example:

with pytest.raises(HTTPError) as error_info:
    make_request()
    assert error_info == exception

Full example:

test_main.py

from unittest import mock
from unittest.mock import patch

import pytest
from requests import HTTPError

from main import make_request


@patch("main.get")
def test_main_exception(mock_get):
    exception = HTTPError(mock.Mock(status=404), "not found")
    mock_get(mock.ANY).raise_for_status.side_effect = exception

    with pytest.raises(HTTPError) as error_info:
        make_request()
        assert error_info == exception

See pytest - Assertions about expected exceptions for more info.

Upvotes: 4

Related Questions