From 313a4f43d36393fc8f3bee1903737714f1c119ca Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sat, 7 Feb 2026 17:14:33 -0800 Subject: [PATCH 1/2] Fix MockTransport not setting elapsed property MockTransport now automatically sets response.elapsed to timedelta(0) for both sync and async requests, matching the behavior of real transports and eliminating the need for boilerplate code in handlers. Previously, accessing response.elapsed on MockTransport responses would raise RuntimeError. Now it returns timedelta(0) by default, which is the expected behavior for mocked requests with no actual network I/O. Fixes #3712 --- httpx/_transports/mock.py | 3 +++ tests/client/test_client.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/httpx/_transports/mock.py b/httpx/_transports/mock.py index 8c418f59e0..9fd5ec689d 100644 --- a/httpx/_transports/mock.py +++ b/httpx/_transports/mock.py @@ -1,5 +1,6 @@ from __future__ import annotations +import datetime import typing from .._models import Request, Response @@ -24,6 +25,7 @@ def handle_request( response = self.handler(request) if not isinstance(response, Response): # pragma: no cover raise TypeError("Cannot use an async handler in a sync Client") + response.elapsed = datetime.timedelta() return response async def handle_async_request( @@ -40,4 +42,5 @@ async def handle_async_request( if not isinstance(response, Response): response = await response + response.elapsed = datetime.timedelta() return response diff --git a/tests/client/test_client.py b/tests/client/test_client.py index 657839018a..535c741fa3 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -460,3 +460,40 @@ def cp1252_but_no_content_type(request): assert response.reason_phrase == "OK" assert response.encoding == "ISO-8859-1" assert response.text == text + + +def test_mock_transport_elapsed(): + """Test that MockTransport sets the elapsed property. + + Regression test for https://github.com/encode/httpx/issues/3712 + """ + def handler(request): + return httpx.Response(200, json={"text": "Hello, world!"}) + + transport = httpx.MockTransport(handler) + with httpx.Client(transport=transport) as client: + response = client.get("http://www.example.com") + assert response.status_code == 200 + # MockTransport should set elapsed to timedelta(0) + assert response.elapsed == timedelta(0) + # Accessing elapsed should not raise RuntimeError + assert isinstance(response.elapsed, timedelta) + + +@pytest.mark.anyio +async def test_async_mock_transport_elapsed(): + """Test that MockTransport sets the elapsed property for async requests. + + Regression test for https://github.com/encode/httpx/issues/3712 + """ + def handler(request): + return httpx.Response(200, json={"text": "Hello, world!"}) + + transport = httpx.MockTransport(handler) + async with httpx.AsyncClient(transport=transport) as client: + response = await client.get("http://www.example.com") + assert response.status_code == 200 + # MockTransport should set elapsed to timedelta(0) + assert response.elapsed == timedelta(0) + # Accessing elapsed should not raise RuntimeError + assert isinstance(response.elapsed, timedelta) From c7216aca6749e0407253133fa3c4e915d935e096 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sat, 7 Feb 2026 18:41:59 -0800 Subject: [PATCH 2/2] Fix code formatting in test_client.py Add blank lines after docstrings in MockTransport elapsed tests to comply with Ruff formatting rules. --- tests/client/test_client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/client/test_client.py b/tests/client/test_client.py index 535c741fa3..90afc7bfc6 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -467,6 +467,7 @@ def test_mock_transport_elapsed(): Regression test for https://github.com/encode/httpx/issues/3712 """ + def handler(request): return httpx.Response(200, json={"text": "Hello, world!"}) @@ -486,6 +487,7 @@ async def test_async_mock_transport_elapsed(): Regression test for https://github.com/encode/httpx/issues/3712 """ + def handler(request): return httpx.Response(200, json={"text": "Hello, world!"})