Skip to main content

http-snapshot is a pytest plugin that snapshots requests made with popular Python HTTP clients.

Project description

http-snapshot

http-snapshot is a pytest plugin that captures and snapshots HTTP requests/responses made with popular Python HTTP clients like httpx and requests. It uses inline-snapshot to store HTTP interactions as JSON files, enabling fast and reliable HTTP testing without making actual network calls.

Features

  • 🚀 Support for multiple HTTP clients: httpx (async, sync) and requests (sync)
  • 📸 Automatic HTTP interaction capture: Records both requests and responses
  • 🔒 Security-aware: Automatically excludes sensitive headers like authorization and cookies
  • ⚙️ Configurable: Control what gets captured and what gets excluded
  • 🧪 pytest integration: Works seamlessly with your existing pytest test suite
  • 📁 External snapshots: Stores snapshots in organized JSON files

Installation

pip install http-snapshot

For specific HTTP client support:

# For httpx support
pip install http-snapshot[httpx]

# For requests support
pip install http-snapshot[requests]

# For both
pip install http-snapshot[httpx,requests]

Quick Start

Using with httpx (async)

import httpx
import pytest
import inline_snapshot

@pytest.mark.anyio
@pytest.mark.parametrize(
    "http_snapshot",
    [inline_snapshot.external("uuid:my-test-snapshot.json")],
)
async def test_api_call(snapshot_async_httpx_client: httpx.AsyncClient) -> None:
    # This will be captured on first run, replayed on subsequent runs
    response = await snapshot_async_httpx_client.get("https://api.example.com/users")
    assert response.status_code == 200
    assert "users" in response.json()

Using with httpx (sync)

import httpx
import pytest
import inline_snapshot

@pytest.mark.anyio
@pytest.mark.parametrize(
    "http_snapshot",
    [inline_snapshot.external("uuid:my-test-snapshot.json")],
)
def test_api_call(snapshot_sync_httpx_client: httpx.Client) -> None:
    # This will be captured on first run, replayed on subsequent runs
    response = snapshot_async_httpx_client.get("https://api.example.com/users")
    assert response.status_code == 200
    assert "users" in response.json()

Using with requests (sync)

import requests
import pytest
import inline_snapshot

@pytest.mark.parametrize(
    "http_snapshot",
    [inline_snapshot.external("uuid:my-test-snapshot.json")],
)
def test_api_call(snapshot_requests_session: requests.Session) -> None:
    # This will be captured on first run, replayed on subsequent runs
    response = snapshot_requests_session.get("https://api.example.com/users")
    assert response.status_code == 200
    assert "users" in response.json()

How It Works

# Capture new snapshots by running pytest with --inline snapshot=create or --inline snapshot=fix.
pytest tests/ --inline-snapshot=create,fix

# Replay existing snapshots (default)
pytest tests/

Configuration Options

You can customize what gets captured using SnapshotSerializerOptions:

import pytest
import inline_snapshot
from http_snapshot._serializer import SnapshotSerializerOptions

@pytest.mark.parametrize(
    "http_snapshot, http_snapshot_serializer_options",
    [
        (
            inline_snapshot.external("uuid:my-test-snapshot.json"),
            SnapshotSerializerOptions(
                exclude_request_headers=["X-API-Key"],
                include_request=True,  # Include request details in snapshot
            ),
        ),
    ],
)
def test_with_custom_options(
    snapshot_requests_session: requests.Session,
    http_snapshot_serializer_options: SnapshotSerializerOptions,
) -> None:
    response = snapshot_requests_session.get(
        "https://api.example.com/protected",
        headers={"X-API-Key": "secret-key"}
    )
    assert response.status_code == 200

Available Options

  • include_request: Whether to include request details in snapshots (default: True)
  • exclude_request_headers: List of request headers to exclude from snapshots
  • exclude_response_headers: List of response headers to exclude from snapshots

By default, the following sensitive headers are always excluded:

  • Request: authorization, cookie
  • Response: set-cookie, www-authenticate, proxy-authenticate, authentication-info, proxy-authentication-info, transfer-encoding, content-encoding

Snapshot Format

Snapshots are stored as JSON files with the following structure:

[
  {
    "request": {
      "method": "GET",
      "url": "https://api.example.com/users",
      "headers": {
        "host": "api.example.com",
        "accept": "*/*",
        "accept-encoding": "gzip, deflate",
        "connection": "keep-alive",
        "user-agent": "python-httpx/0.28.1"
      },
      "body": ""
    },
    "response": {
      "status_code": 200,
      "headers": {
        "date": "Thu, 21 Aug 2025 15:49:45 GMT",
        "content-type": "application/json; charset=utf-8",
        "connection": "keep-alive",
        "server": "nginx/1.18.0"
      },
      "body": {
        "users": [
          {
            "id": 1,
            "name": "John Doe",
            "email": "john@example.com"
          },
          {
            "id": 2,
            "name": "Jane Smith",
            "email": "jane@example.com"
          }
        ]
      }
    }
  }
]

Content Encoding

The plugin intelligently handles different content types:

  • JSON: Formatted with proper indentation for readability
  • Text: Stored as UTF-8 strings
  • Binary: Base64 encoded

Advanced Examples

Testing API with Multiple Requests

@pytest.mark.anyio
@pytest.mark.parametrize(
    "http_snapshot",
    [inline_snapshot.external("uuid:multi-request-test.json")],
)
async def test_multiple_requests(snapshot_async_httpx_client: httpx.AsyncClient) -> None:
    # Create a user
    create_response = await snapshot_async_httpx_client.post(
        "https://api.example.com/users",
        json={"name": "Alice", "email": "alice@example.com"}
    )
    assert create_response.status_code == 201
    user_id = create_response.json()["id"]

    # Fetch the user
    get_response = await snapshot_async_httpx_client.get(
        f"https://api.example.com/users/{user_id}"
    )
    assert get_response.status_code == 200
    assert get_response.json()["name"] == "Alice"

Testing with Authentication

@pytest.mark.parametrize(
    "http_snapshot, http_snapshot_serializer_options",
    [
        (
            inline_snapshot.external("uuid:auth-test.json"),
            SnapshotSerializerOptions(exclude_request_headers=["Authorization"]),
        ),
    ],
)
def test_authenticated_request(
    snapshot_requests_session: requests.Session,
    http_snapshot_serializer_options,
) -> None:
    # The Authorization header will be excluded from the snapshot
    response = snapshot_requests_session.get(
        "https://api.example.com/profile",
        headers={"Authorization": "Bearer secret-token"}
    )
    assert response.status_code == 200

Best Practices

  1. Exclude sensitive data: Always exclude headers containing secrets, tokens, or personal data
  2. Review snapshots: Check generated snapshot files into version control and review changes

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

http_snapshot-0.1.3.tar.gz (10.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

http_snapshot-0.1.3-py3-none-any.whl (10.6 kB view details)

Uploaded Python 3

File details

Details for the file http_snapshot-0.1.3.tar.gz.

File metadata

  • Download URL: http_snapshot-0.1.3.tar.gz
  • Upload date:
  • Size: 10.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for http_snapshot-0.1.3.tar.gz
Algorithm Hash digest
SHA256 1fbf5f2c856a30607158d2571aa28c93134e825e5e7061eaf91f3e9fc0dba36e
MD5 cde988711489829546f2c5e3a14b03fb
BLAKE2b-256 cb5c0c2b18bc5f32304831c72ffddf9c614765d23fb2e4e6db76ff472fe71d62

See more details on using hashes here.

Provenance

The following attestation bundles were made for http_snapshot-0.1.3.tar.gz:

Publisher: publish.yml on karpetrosyan/http-snapshot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file http_snapshot-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: http_snapshot-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 10.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for http_snapshot-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ef775af60b7a7876a4f6a5f383d49e63bf7e99e6c28acf53cc207fabaf131693
MD5 09727c3de4031e64f43f4575828be517
BLAKE2b-256 16729ff98d9e4d0b1ae3f2145304a4c4af49da8896d789bce5e6828a796ff450

See more details on using hashes here.

Provenance

The following attestation bundles were made for http_snapshot-0.1.3-py3-none-any.whl:

Publisher: publish.yml on karpetrosyan/http-snapshot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page