Integration Testing with Real API Stubbing in Pytest

Integration testing ensures that different parts of an application work together as expected. When an application interacts with external services, real API calls can slow down tests, introduce dependencies, and make the testing process unreliable. To mitigate these issues while keeping tests close to real-world scenarios, API stubbing is used.

In this blog, we’ll cover:

  • What API stubbing is and why it’s useful
  • How it differs from mocking
  • Popular techniques for API stubbing in Pytest
  • Tools like responses, httpretty, and WireMock
  • Best practices for integration testing with stubbing
  • Useful resources, GitHub repositories, and AI tools

πŸš€ What is API Stubbing?

API stubbing is the process of replacing real API calls with predefined responses in integration tests. Unlike unit test mocks, stubs provide more realistic behavior and are often used when testing a fully integrated application instead of individual functions.

πŸ†š Mocking vs. Stubbing

Feature Mocking (Unit Test) Stubbing (Integration Test)
Purpose Isolate unit tests Simulate real API behavior
Scope Function level System or module level
Realism Returns predefined results Mimics API responses more closely
Dependency No dependency on external service May require API contract compliance
Tools unittest.mock, pytest-mock responses, httpretty, WireMock, MockServer

Β 

βœ… Using responses for API Stubbing in Pytest

1️⃣ The Function to be Tested

Let’s assume we have a function that fetches user details from an external API.

import requests

def get_user_info(user_id):
    """Fetches user data from an external service."""
    url = f"https://api.example.com/users/{user_id}"
    response = requests.get(url)
    
    if response.status_code == 200:
        return response.json()
    else:
        return {"error": "User not found"}

2️⃣ Stubbing the API Using responses

The responses library allows you to intercept HTTP requests and return predefined responses.

πŸ”Ή How It Works

  • @responses.activate enables request interception.
  • responses.add() defines a stubbed API response.
  • The function under test makes an HTTP request, but responses intercepts it and returns the predefined response.
# pip install responses

import responses
import pytest
from mymodule import get_user_info  # Import the function under test

@responses.activate
def test_get_user_info():
    """Integration test with API stubbing using responses."""
    
    # Define the stubbed response
    responses.add(
        responses.GET,
        "https://api.example.com/users/123",
        json={"id": 123, "name": "John Doe", "email": "john@example.com"},
        status=200
    )

    # Call the function and verify the response
    response = get_user_info(123)
    assert response == {"id": 123, "name": "John Doe", "email": "john@example.com"}

πŸ”₯ Handling Edge Cases in Stubbing

1️⃣ Simulating an API Failure (500 Error)

@responses.activate
def test_get_user_info_api_failure():
    responses.add(
        responses.GET,
        "https://api.example.com/users/123",
        json={"error": "Internal Server Error"},
        status=500
    )

    response = get_user_info(123)
    assert response == {"error": "User not found"}

2️⃣ Simulating a Timeout

import requests

@responses.activate
def test_get_user_info_timeout():
    responses.add(
        responses.GET,
        "https://api.example.com/users/123",
        body=requests.exceptions.Timeout()
    )

    with pytest.raises(requests.exceptions.Timeout):
        get_user_info(123)

3️⃣ Simulating API Rate Limiting (429 Error)

@responses.activate
def test_get_user_info_rate_limit():
    responses.add(
        responses.GET,
        "https://api.example.com/users/123",
        json={"error": "Too many requests"},
        status=429
    )

    response = get_user_info(123)
    assert response == {"error": "User not found"}

🎯 Using httpretty for API Stubbing

Another popular library for API stubbing is httpretty, which works similarly to responses but offers more flexibility.

πŸ”Ή httpretty Advantages

  • Supports dynamic response modification (useful for real-time testing).
  • Can mock both HTTP and HTTPS requests.
  • Works well with third-party HTTP clients like urllib3.
# pip install httpretty

import httpretty
import pytest
from mymodule import get_user_info

@httpretty.activate
def test_get_user_info_with_httpretty():
    """Integration test with API stubbing using httpretty."""
    
    httpretty.register_uri(
        httpretty.GET,
        "https://api.example.com/users/123",
        body='{"id": 123, "name": "Alice", "email": "alice@example.com"}',
        content_type="application/json"
    )

    response = get_user_info(123)
    assert response == {"id": 123, "name": "Alice", "email": "alice@example.com"}

πŸ”„ Using WireMock for Advanced Stubbing

For more complex API stubbing, especially when testing microservices, WireMock (a standalone API mocking tool) is useful.

Setting Up WireMock

  1. Install WireMock in Docker

  2. Define an API Stub in WireMock

  3. Run the integration test by making a request to http://localhost:8080/users/123 instead of the real API.

πŸ”Ή WireMock Benefits

βœ… Supports stateful API stubbing.
βœ… Allows recording & replaying real API interactions.
βœ… Can be used locally or in CI/CD pipelines.

# docker run -d -p 8080:8080 wiremock/wiremock

curl -X POST http://localhost:8080/__admin/mappings -H "Content-Type: application/json" -d '{
    "request": {
        "method": "GET",
        "url": "/users/123"
    },
    "response": {
        "status": 200,
        "body": "{\"id\": 123, \"name\": \"Bob\"}"
    }
}'

πŸ† Best Practices for API Stubbing in Pytest

βœ… Use responses for lightweight stubbing – Ideal for simple tests.
βœ… Use httpretty for compatibility with multiple HTTP clients.
βœ… Use WireMock for more complex API interactions.
βœ… Ensure stubbed responses match real API contracts – This avoids unexpected issues in production.
βœ… Test failure scenarios – Always simulate timeouts, rate limits, and server failures.


πŸ“š Further Reading & Resources

πŸ”— Popular Resources

🎯 Underrated Resources

πŸ“Œ Popular GitHub Repositories

πŸ€– AI Tools for Improved Testing

  • Mockoon – No-code API mocking
  • Postman Mock Server – API stubbing & contract validation
Search

Table of Contents

You may also like to read