DEV Community

Felipe Henrique Gross Windmoller
Felipe Henrique Gross Windmoller

Posted on

8

Python FastAPI: Tutorial to Test HTTP Client Requests

Testing HTTP Client Requests

Disclaimer: I’m learning Python, so please correct me if I’ve written something wrong!

In this tutorial, we’ll explore how to test HTTP client calls in a FastAPI Python application using two different methods:

  • unittest.mock: This is a built-in Python library used for mocking objects in tests. It allows you to replace parts of your system under test and make assertions about how they have been used.
  • httpretty: This is a third-party library that allows you to mock HTTP requests at a low level by creating a fake HTTP server.

To run the tests, just execute this on the root of the project directory:



pytest


Enter fullscreen mode Exit fullscreen mode

You should see output similar to this:



$ pytest
============================================================================================== test session starts ==============================================================================================
platform linux -- Python 3.11.6, pytest-8.2.0, pluggy-1.5.0
rootdir: /home/wsl/github/python-study/api-client
plugins: anyio-4.2.0
collected 3 items

tests/test_main.py .                                                                                                                                                                                      [ 33%]
tests/test_main_httppretty_mock.py .                                                                                                                                                                      [ 66%]
tests/test_main_magic_mock.py .                                                                                                                                                                           [100%]

=============================================================================================== 3 passed in 0.39s ===============================================================================================


Enter fullscreen mode Exit fullscreen mode

This output indicates that all three tests passed successfully. The percentage in brackets shows the progress of the test run.

Versions of my modules



$ pip list
Package                            Version
---------------------------------- -----------
fastapi                            0.111.0
httpretty                          1.1.4
pydantic                           2.5.3
pytest                             8.2.0
requests                           2.31.0


Enter fullscreen mode Exit fullscreen mode

Project structure



.
├── app
│   ├── __init__.py
│   └── main.py
└── tests
    ├── __init__.py
    ├── test_main_httppretty_mock.py
    └── test_main_magic_mock.py


Enter fullscreen mode Exit fullscreen mode

__init__.py are just empty files

main.py



from pydantic import BaseModel
from fastapi import FastAPI, HTTPException
from typing import Union
import requests
import os
import logging

API_SERVER_URL = os.getenv("API_SERVER_URL", default="http://localhost:8010")
API_LOG_LEVEL = os.getenv("API_LOG_LEVEL", default="INFO")

# Configure basic logging
logging.basicConfig(level=API_LOG_LEVEL)
logger = logging.getLogger()

app = FastAPI()


class Item(BaseModel):
    name: str
    price: float
    is_offer: Union[bool, None] = None


@app.get("/")
def read_root():
    return {"Hello": "World"}


@app.get("/items/{item_id}", response_model=Item)
def read_item(item_id: int, q: Union[str, None] = None):
    try:
        response = requests.get(
            f"{API_SERVER_URL}/items/{item_id}", params={"q": q})
        # Raises HTTPError for bad responses (4XX or 5XX)
        response.raise_for_status()
        item_data = response.json()
        item = Item(**item_data)  # Deserialize the JSON into an Item object
        return item
    except requests.RequestException as e:
        logger.error(f"Request failed: {str(e)}")  # Log the error
        raise HTTPException(status_code=500, detail=str(e))



Enter fullscreen mode Exit fullscreen mode

test_main_magic_mock.py



from fastapi.testclient import TestClient
import pytest
from unittest.mock import patch, MagicMock
from app.main import app

client = TestClient(app)


@pytest.fixture
def mock_requests_get():
    # Create a MagicMock object to mock requests.get
    with patch('requests.get') as mock_get:
        # Prepare a mock response object with necessary methods
        mock_response = MagicMock()
        mock_response.json.return_value = {
            "name": "Sample Item",
            "price": 100.0,
            "is_offer": None
        }
        mock_response.raise_for_status = MagicMock()
        mock_get.return_value = mock_response
        yield mock_get


# The mock_requests_get fixture is automatically used here.
def test_read_item(mock_requests_get):
    response = client.get("/items/1")
    assert response.status_code == 200
    assert response.json() == {
        "name": "Sample Item",
        "price": 100.0,
        "is_offer": None
    }
    # Assert if requests.get was called correctly
    mock_requests_get.assert_called_once_with(
        "http://localhost:8010/items/1",
        params={'q': None}
    )


Enter fullscreen mode Exit fullscreen mode

test_main_httppretty_mock.py



import httpretty
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)


@httpretty.activate
def test_read_item():
    # Mocking the external API call
    httpretty.register_uri(
        httpretty.GET,
        "http://localhost:8010/items/1",
        body='{"name": "Sample Item", "price": 100.0, "is_offer": null}',
        content_type="application/json",
        status=200  # Define the expected status code
    )

    # Call the endpoint
    response = client.get("/items/1")

    # Assertions
    assert response.status_code == 200
    assert response.json() == {
        "name": "Sample Item",
        "price": 100.0,
        "is_offer": None
    }


Enter fullscreen mode Exit fullscreen mode

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay