Skip to main content

Build Status PyPI

mock-response-delay

Simulate a slow server, and the client timeout it causes, in HTTP mocks.

responses and respx answer requests instantly, so a test of how code handles a slow server, or a timeout, has nothing to exercise. This package wraps a mock’s callback so that it answers as a server which takes a given number of seconds would. A request whose read timeout is shorter than the delay waits for the timeout and then raises the same exception which the client library raises against a real slow server. Any other request gets the response after waiting for the delay.

The waiting is done by a function which you can replace, so a test can record the waits, or advance a fake clock, instead of really sleeping.

Installation

Install the extra for the client library which the code under test uses:

pip install 'mock-response-delay[requests]'
pip install 'mock-response-delay[httpx]'
pip install 'mock-response-delay[httpx2]'

This requires Python 3.12+.

Usage

requests with responses

Wrap a callback before giving it to responses. A request with a read timeout shorter than the delay raises requests.exceptions.Timeout:

"""Time out against a slow server."""

import pytest
import requests
import responses
from requests import PreparedRequest

from mock_response_delay.for_requests import delayed_responses_callback


def slow_callback(request: PreparedRequest) -> tuple[int, dict[str, str], str]:
    """Answer any request."""
    del request
    return (200, {}, "Hello")


waits: list[float] = []

with responses.RequestsMock() as mock:
    _registration = mock.add_callback(
        method="GET",
        url="https://example.com/",
        callback=delayed_responses_callback(
            callback=slow_callback,
            delay_seconds=5.0,
            sleep_fn=waits.append,
        ),
    )

    with pytest.raises(expected_exception=requests.exceptions.Timeout):
        _response = requests.get(url="https://example.com/", timeout=1.0)

    response = requests.get(url="https://example.com/", timeout=10.0)

assert response.text == "Hello"
assert waits == [1.0, 5.0]

requests accepts the timeout as one number, or as a (connect, read) tuple. A slow server only affects the read leg, so only the read timeout is compared with the delay.

httpx with respx or httpx.MockTransport

Wrap a handler before giving it to httpx.MockTransport, or to respx as a side effect. A request with a read timeout shorter than the delay raises httpx.ReadTimeout:

"""Time out against a slow server."""

import httpx
import pytest

from mock_response_delay.for_httpx import delayed_httpx_handler


def slow_handler(request: httpx.Request) -> httpx.Response:
    """Answer any request."""
    del request
    return httpx.Response(status_code=200, text="Hello")


waits: list[float] = []
transport = httpx.MockTransport(
    handler=delayed_httpx_handler(
        handler=slow_handler,
        delay_seconds=5.0,
        sleep_fn=waits.append,
    ),
)

with httpx.Client(transport=transport) as client:
    with pytest.raises(expected_exception=httpx.ReadTimeout):
        _response = client.get(url="https://example.com/", timeout=1.0)

    response = client.get(url="https://example.com/", timeout=10.0)

assert response.text == "Hello"
assert waits == [1.0, 5.0]

httpx2

httpx2 has its own request, response and exception classes, so it has its own module, which works in the same way with httpx2.MockTransport:

"""Time out against a slow server."""

import httpx2
import pytest

from mock_response_delay.for_httpx2 import delayed_httpx2_handler


def slow_handler(request: httpx2.Request) -> httpx2.Response:
    """Answer any request."""
    del request
    return httpx2.Response(status_code=200, text="Hello")


waits: list[float] = []
transport = httpx2.MockTransport(
    handler=delayed_httpx2_handler(
        handler=slow_handler,
        delay_seconds=5.0,
        sleep_fn=waits.append,
    ),
)

with httpx2.Client(transport=transport) as client:
    with pytest.raises(expected_exception=httpx2.ReadTimeout):
        _response = client.get(url="https://example.com/", timeout=1.0)

    response = client.get(url="https://example.com/", timeout=10.0)

assert response.text == "Hello"
assert waits == [1.0, 5.0]

Controlling the clock

sleep_fn defaults to time.sleep, so by default the delay is real. The examples above record the waits instead, which keeps the test instant. Something which advances a fake clock, such as a freezegun tick, works the same way.

Full documentation

See the full documentation.

Metadata

Release files for mock-response-delay 2026.9.12

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mock-response-delay 2026.9.12
File Size Uploaded
mock_response_delay-2026.9.12.tar.gz 153.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mock-response-delay 2026.9.12
File Interpreter ABI Platform
mock_response_delay-2026.9.12-py3-none-any.whl Python 3 none any Details

Total release size: 163.2 kB

Release files / mock_response_delay-2026.9.12.tar.gz

Download URL mock_response_delay-2026.9.12.tar.gz
Size 153.6 kB
Tags Source
SHA-256 checksum
How to use checksums
c48841a7f9e6f49eadc76cd789dc9d5894d70ab2a89ae64f82567f4bc890a8bb
BLAKE2b-256 checksum
How to use checksums
55879639579dc4ce58953c51add29336c3fa5d0fffe8e14e6903e79e8028a74b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release files / mock_response_delay-2026.9.12-py3-none-any.whl

Download URL mock_response_delay-2026.9.12-py3-none-any.whl
Size 9.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7ba63886d202eb9c2c0508cf650d8ce314cf404e0b3f66e748ebf244a3441fc9
BLAKE2b-256 checksum
How to use checksums
824541adceb85da9e1640b534ded333fb2be2d7256b6a1ef8b46d8d6bd215882
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2026.9.12 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page