]> git.ipfire.org Git - thirdparty/httpx.git/commitdiff
Add `httpx.MockTransport()` (#1401)
authorTom Christie <tom@tomchristie.com>
Wed, 6 Jan 2021 11:04:26 +0000 (11:04 +0000)
committerGitHub <noreply@github.com>
Wed, 6 Jan 2021 11:04:26 +0000 (11:04 +0000)
* Add httpx.MockTransport

* Add docs on MockTransport

* Add pointer to RESPX

* Add note on pytest-httpx

* Tweak existing docs example to use 'httpx.MockTransport'

Co-authored-by: Florimond Manca <florimond.manca@gmail.com>
13 files changed:
docs/advanced.md
httpx/__init__.py
httpx/_transports/mock.py [new file with mode: 0644]
tests/client/test_async_client.py
tests/client/test_auth.py
tests/client/test_client.py
tests/client/test_cookies.py
tests/client/test_event_hooks.py
tests/client/test_headers.py
tests/client/test_queryparams.py
tests/client/test_redirects.py
tests/test_multipart.py
tests/utils.py

index 837acd4941722defb30d045cf887aaba6ad4f4fc..5463250f11cfa5d15083f13c8999abff13a24494 100644 (file)
@@ -1062,6 +1062,31 @@ Which we can use in the same way:
 {"text": "Hello, world!"}
 ```
 
+### Mock transports
+
+During testing it can often be useful to be able to mock out a transport,
+and return pre-determined responses, rather than making actual network requests.
+
+The `httpx.MockTransport` class accepts a handler function, which can be used
+to map requests onto pre-determined responses:
+
+```python
+def handler(request):
+    return httpx.Response(200, json={"text": "Hello, world!"})
+
+
+# Switch to a mock transport, if the TESTING environment variable is set.
+if os.environ['TESTING'].upper() == "TRUE":
+    transport = httpx.MockTransport(handler)
+else:
+    transport = httpx.HTTPTransport()
+
+client = httpx.Client(transport=transport)
+```
+
+For more advanced use-cases you might want to take a look at either [the third-party
+mocking library, RESPX](https://lundberg.github.io/respx/), or the [pytest-httpx library](https://github.com/Colin-b/pytest_httpx).
+
 ### Mounting transports
 
 You can also mount transports against given schemes or domains, to control
@@ -1101,7 +1126,10 @@ Mocking requests to a given domain:
 ```python
 # All requests to "example.org" should be mocked out.
 # Other requests occur as usual.
-mounts = {"all://example.org": MockTransport()}
+def handler(request):
+    return httpx.Response(200, json={"text": "Hello, World!"})
+
+mounts = {"all://example.org": httpx.MockTransport(handler)}
 client = httpx.Client(mounts=mounts)
 ```
 
index 489ffeae4fc2375a7ed0c57ec5663cc8d7f76ed1..8a6d9b32cec8fbd05f94fa3d77d57b37d43fbd91 100644 (file)
@@ -36,6 +36,7 @@ from ._exceptions import (
 from ._models import URL, Cookies, Headers, QueryParams, Request, Response
 from ._status_codes import StatusCode, codes
 from ._transports.asgi import ASGITransport
+from ._transports.mock import MockTransport
 from ._transports.wsgi import WSGITransport
 
 __all__ = [
@@ -65,6 +66,7 @@ __all__ = [
     "InvalidURL",
     "Limits",
     "LocalProtocolError",
+    "MockTransport",
     "NetworkError",
     "options",
     "patch",
diff --git a/httpx/_transports/mock.py b/httpx/_transports/mock.py
new file mode 100644 (file)
index 0000000..6f9ebc1
--- /dev/null
@@ -0,0 +1,56 @@
+from typing import Callable, List, Optional, Tuple
+
+import httpcore
+
+from .._models import Request
+
+
+class MockTransport(httpcore.SyncHTTPTransport, httpcore.AsyncHTTPTransport):
+    def __init__(self, handler: Callable) -> None:
+        self.handler = handler
+
+    def request(
+        self,
+        method: bytes,
+        url: Tuple[bytes, bytes, Optional[int], bytes],
+        headers: List[Tuple[bytes, bytes]] = None,
+        stream: httpcore.SyncByteStream = None,
+        ext: dict = None,
+    ) -> Tuple[int, List[Tuple[bytes, bytes]], httpcore.SyncByteStream, dict]:
+        request = Request(
+            method=method,
+            url=url,
+            headers=headers,
+            stream=stream,
+        )
+        request.read()
+        response = self.handler(request)
+        return (
+            response.status_code,
+            response.headers.raw,
+            response.stream,
+            response.ext,
+        )
+
+    async def arequest(
+        self,
+        method: bytes,
+        url: Tuple[bytes, bytes, Optional[int], bytes],
+        headers: List[Tuple[bytes, bytes]] = None,
+        stream: httpcore.AsyncByteStream = None,
+        ext: dict = None,
+    ) -> Tuple[int, List[Tuple[bytes, bytes]], httpcore.AsyncByteStream, dict]:
+        request = Request(
+            method=method,
+            url=url,
+            headers=headers,
+            stream=stream,
+        )
+        await request.aread()
+        response = self.handler(request)
+        return (
+            response.status_code,
+            response.headers.raw,
+            response.stream,
+            response.ext,
+        )
index 696f202cff9aa36870b02a1668eae6751ff288a7..0cc9d8a4ff1458f9949948c4e5e52ae7ebeadf79 100644 (file)
@@ -5,7 +5,6 @@ import httpcore
 import pytest
 
 import httpx
-from tests.utils import MockTransport
 
 
 @pytest.mark.usefixtures("async_environment")
@@ -247,7 +246,7 @@ def hello_world(request):
 
 @pytest.mark.usefixtures("async_environment")
 async def test_client_closed_state_using_implicit_open():
-    client = httpx.AsyncClient(transport=MockTransport(hello_world))
+    client = httpx.AsyncClient(transport=httpx.MockTransport(hello_world))
 
     assert not client.is_closed
     await client.get("http://example.com")
@@ -262,7 +261,7 @@ async def test_client_closed_state_using_implicit_open():
 
 @pytest.mark.usefixtures("async_environment")
 async def test_client_closed_state_using_with_block():
-    async with httpx.AsyncClient(transport=MockTransport(hello_world)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(hello_world)) as client:
         assert not client.is_closed
         await client.get("http://example.com")
 
@@ -273,7 +272,7 @@ async def test_client_closed_state_using_with_block():
 
 @pytest.mark.usefixtures("async_environment")
 async def test_deleting_unclosed_async_client_causes_warning():
-    client = httpx.AsyncClient(transport=MockTransport(hello_world))
+    client = httpx.AsyncClient(transport=httpx.MockTransport(hello_world))
     await client.get("http://example.com")
     with pytest.warns(UserWarning):
         del client
@@ -291,8 +290,8 @@ def mounted(request: httpx.Request) -> httpx.Response:
 
 @pytest.mark.usefixtures("async_environment")
 async def test_mounted_transport():
-    transport = MockTransport(unmounted)
-    mounts = {"custom://": MockTransport(mounted)}
+    transport = httpx.MockTransport(unmounted)
+    mounts = {"custom://": httpx.MockTransport(mounted)}
 
     async with httpx.AsyncClient(transport=transport, mounts=mounts) as client:
         response = await client.get("https://www.example.com")
index 677fb9f6194695d6b5892f89c99fa1d6c9a1b1fb..c41afeff870790686d893bc417ec5ef84bcc60f3 100644 (file)
@@ -13,7 +13,6 @@ import pytest
 
 import httpx
 from httpx import URL, Auth, BasicAuth, DigestAuth, ProtocolError, Request, Response
-from tests.utils import MockTransport
 
 from ..common import FIXTURES_DIR
 
@@ -155,7 +154,7 @@ async def test_basic_auth() -> None:
     auth = ("tomchristie", "password123")
     app = App()
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -171,7 +170,9 @@ async def test_basic_auth_with_stream() -> None:
     auth = ("tomchristie", "password123")
     app = App()
 
-    async with httpx.AsyncClient(transport=MockTransport(app), auth=auth) as client:
+    async with httpx.AsyncClient(
+        transport=httpx.MockTransport(app), auth=auth
+    ) as client:
         async with client.stream("GET", url) as response:
             await response.aread()
 
@@ -184,7 +185,7 @@ async def test_basic_auth_in_url() -> None:
     url = "https://tomchristie:password123@example.org/"
     app = App()
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url)
 
     assert response.status_code == 200
@@ -197,7 +198,9 @@ async def test_basic_auth_on_session() -> None:
     auth = ("tomchristie", "password123")
     app = App()
 
-    async with httpx.AsyncClient(transport=MockTransport(app), auth=auth) as client:
+    async with httpx.AsyncClient(
+        transport=httpx.MockTransport(app), auth=auth
+    ) as client:
         response = await client.get(url)
 
     assert response.status_code == 200
@@ -213,7 +216,7 @@ async def test_custom_auth() -> None:
         request.headers["Authorization"] = "Token 123"
         return request
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -226,7 +229,7 @@ async def test_netrc_auth() -> None:
     url = "http://netrcexample.org"
     app = App()
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url)
 
     assert response.status_code == 200
@@ -241,7 +244,7 @@ async def test_auth_header_has_priority_over_netrc() -> None:
     url = "http://netrcexample.org"
     app = App()
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, headers={"Authorization": "Override"})
 
     assert response.status_code == 200
@@ -255,7 +258,7 @@ async def test_trust_env_auth() -> None:
     app = App()
 
     async with httpx.AsyncClient(
-        transport=MockTransport(app), trust_env=False
+        transport=httpx.MockTransport(app), trust_env=False
     ) as client:
         response = await client.get(url)
 
@@ -263,7 +266,7 @@ async def test_trust_env_auth() -> None:
     assert response.json() == {"auth": None}
 
     async with httpx.AsyncClient(
-        transport=MockTransport(app), trust_env=True
+        transport=httpx.MockTransport(app), trust_env=True
     ) as client:
         response = await client.get(url)
 
@@ -279,7 +282,9 @@ async def test_auth_disable_per_request() -> None:
     auth = ("tomchristie", "password123")
     app = App()
 
-    async with httpx.AsyncClient(transport=MockTransport(app), auth=auth) as client:
+    async with httpx.AsyncClient(
+        transport=httpx.MockTransport(app), auth=auth
+    ) as client:
         response = await client.get(url, auth=None)
 
     assert response.status_code == 200
@@ -299,7 +304,7 @@ async def test_auth_hidden_header() -> None:
     auth = ("example-username", "example-password")
     app = App()
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert "'authorization': '[secure]'" in str(response.request.headers)
@@ -309,7 +314,7 @@ async def test_auth_hidden_header() -> None:
 async def test_auth_property() -> None:
     app = App()
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         assert client.auth is None
 
         client.auth = ("tomchristie", "password123")  # type: ignore
@@ -327,11 +332,11 @@ async def test_auth_invalid_type() -> None:
 
     with pytest.raises(TypeError):
         client = httpx.AsyncClient(
-            transport=MockTransport(app),
+            transport=httpx.MockTransport(app),
             auth="not a tuple, not a callable",  # type: ignore
         )
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         with pytest.raises(TypeError):
             await client.get(auth="not a tuple, not a callable")  # type: ignore
 
@@ -345,7 +350,7 @@ async def test_digest_auth_returns_no_auth_if_no_digest_header_in_response() ->
     auth = DigestAuth(username="tomchristie", password="password123")
     app = App()
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -359,7 +364,7 @@ def test_digest_auth_returns_no_auth_if_alternate_auth_scheme() -> None:
     auth_header = "Token ..."
     app = App(auth_header=auth_header, status_code=401)
 
-    client = httpx.Client(transport=MockTransport(app))
+    client = httpx.Client(transport=httpx.MockTransport(app))
     response = client.get(url, auth=auth)
 
     assert response.status_code == 401
@@ -374,7 +379,7 @@ async def test_digest_auth_200_response_including_digest_auth_header() -> None:
     auth_header = 'Digest realm="realm@host.com",qop="auth",nonce="abc",opaque="xyz"'
     app = App(auth_header=auth_header, status_code=200)
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -388,7 +393,7 @@ async def test_digest_auth_401_response_without_digest_auth_header() -> None:
     auth = DigestAuth(username="tomchristie", password="password123")
     app = App(auth_header="", status_code=401)
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 401
@@ -417,7 +422,7 @@ async def test_digest_auth(
     auth = DigestAuth(username="tomchristie", password="password123")
     app = DigestApp(algorithm=algorithm)
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -448,7 +453,7 @@ async def test_digest_auth_no_specified_qop() -> None:
     auth = DigestAuth(username="tomchristie", password="password123")
     app = DigestApp(qop="")
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -480,7 +485,7 @@ async def test_digest_auth_qop_including_spaces_and_auth_returns_auth(qop: str)
     auth = DigestAuth(username="tomchristie", password="password123")
     app = DigestApp(qop=qop)
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -493,7 +498,7 @@ async def test_digest_auth_qop_auth_int_not_implemented() -> None:
     auth = DigestAuth(username="tomchristie", password="password123")
     app = DigestApp(qop="auth-int")
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         with pytest.raises(NotImplementedError):
             await client.get(url, auth=auth)
 
@@ -504,7 +509,7 @@ async def test_digest_auth_qop_must_be_auth_or_auth_int() -> None:
     auth = DigestAuth(username="tomchristie", password="password123")
     app = DigestApp(qop="not-auth")
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         with pytest.raises(ProtocolError):
             await client.get(url, auth=auth)
 
@@ -515,7 +520,7 @@ async def test_digest_auth_incorrect_credentials() -> None:
     auth = DigestAuth(username="tomchristie", password="password123")
     app = DigestApp(send_response_after_attempt=2)
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 401
@@ -537,7 +542,7 @@ async def test_async_digest_auth_raises_protocol_error_on_malformed_header(
     auth = DigestAuth(username="tomchristie", password="password123")
     app = App(auth_header=auth_header, status_code=401)
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         with pytest.raises(ProtocolError):
             await client.get(url, auth=auth)
 
@@ -556,7 +561,7 @@ def test_sync_digest_auth_raises_protocol_error_on_malformed_header(
     auth = DigestAuth(username="tomchristie", password="password123")
     app = App(auth_header=auth_header, status_code=401)
 
-    with httpx.Client(transport=MockTransport(app)) as client:
+    with httpx.Client(transport=httpx.MockTransport(app)) as client:
         with pytest.raises(ProtocolError):
             client.get(url, auth=auth)
 
@@ -571,7 +576,7 @@ async def test_async_auth_history() -> None:
     auth = RepeatAuth(repeat=2)
     app = App(auth_header="abc")
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -597,7 +602,7 @@ def test_sync_auth_history() -> None:
     auth = RepeatAuth(repeat=2)
     app = App(auth_header="abc")
 
-    with httpx.Client(transport=MockTransport(app)) as client:
+    with httpx.Client(transport=httpx.MockTransport(app)) as client:
         response = client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -623,7 +628,7 @@ async def test_digest_auth_unavailable_streaming_body():
     async def streaming_body():
         yield b"Example request body"  # pragma: nocover
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         with pytest.raises(httpx.StreamConsumed):
             await client.post(url, data=streaming_body(), auth=auth)
 
@@ -638,7 +643,7 @@ async def test_async_auth_reads_response_body() -> None:
     auth = ResponseBodyAuth("xyz")
     app = App()
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -654,7 +659,7 @@ def test_sync_auth_reads_response_body() -> None:
     auth = ResponseBodyAuth("xyz")
     app = App()
 
-    with httpx.Client(transport=MockTransport(app)) as client:
+    with httpx.Client(transport=httpx.MockTransport(app)) as client:
         response = client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -672,7 +677,7 @@ async def test_async_auth() -> None:
     auth = SyncOrAsyncAuth()
     app = App()
 
-    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -687,7 +692,7 @@ def test_sync_auth() -> None:
     auth = SyncOrAsyncAuth()
     app = App()
 
-    with httpx.Client(transport=MockTransport(app)) as client:
+    with httpx.Client(transport=httpx.MockTransport(app)) as client:
         response = client.get(url, auth=auth)
 
     assert response.status_code == 200
index e4bf46b9a47aadfdb9780794f45a8892f0463e7b..13bb7f03ad049ca653c148ff9325412b96b8a7ba 100644 (file)
@@ -5,7 +5,6 @@ import httpcore
 import pytest
 
 import httpx
-from tests.utils import MockTransport
 
 
 def test_get(server):
@@ -292,7 +291,7 @@ def hello_world(request):
 
 
 def test_client_closed_state_using_implicit_open():
-    client = httpx.Client(transport=MockTransport(hello_world))
+    client = httpx.Client(transport=httpx.MockTransport(hello_world))
 
     assert not client.is_closed
     client.get("http://example.com")
@@ -306,7 +305,7 @@ def test_client_closed_state_using_implicit_open():
 
 
 def test_client_closed_state_using_with_block():
-    with httpx.Client(transport=MockTransport(hello_world)) as client:
+    with httpx.Client(transport=httpx.MockTransport(hello_world)) as client:
         assert not client.is_closed
         client.get("http://example.com")
 
@@ -330,7 +329,9 @@ def test_raw_client_header():
     url = "http://example.org/echo_headers"
     headers = {"Example-Header": "example-value"}
 
-    client = httpx.Client(transport=MockTransport(echo_raw_headers), headers=headers)
+    client = httpx.Client(
+        transport=httpx.MockTransport(echo_raw_headers), headers=headers
+    )
     response = client.get(url)
 
     assert response.status_code == 200
@@ -355,8 +356,8 @@ def mounted(request: httpx.Request) -> httpx.Response:
 
 
 def test_mounted_transport():
-    transport = MockTransport(unmounted)
-    mounts = {"custom://": MockTransport(mounted)}
+    transport = httpx.MockTransport(unmounted)
+    mounts = {"custom://": httpx.MockTransport(mounted)}
 
     client = httpx.Client(transport=transport, mounts=mounts)
 
@@ -370,7 +371,7 @@ def test_mounted_transport():
 
 
 def test_all_mounted_transport():
-    mounts = {"all://": MockTransport(mounted)}
+    mounts = {"all://": httpx.MockTransport(mounted)}
 
     client = httpx.Client(mounts=mounts)
 
index feb26ac4365087f863287fcaee214c77bc2925fb..fe9125fa062118b857187d58b9d337af71e149e2 100644 (file)
@@ -1,7 +1,6 @@
 from http.cookiejar import Cookie, CookieJar
 
 import httpx
-from tests.utils import MockTransport
 
 
 def get_and_set_cookies(request: httpx.Request) -> httpx.Response:
@@ -21,7 +20,7 @@ def test_set_cookie() -> None:
     url = "http://example.org/echo_cookies"
     cookies = {"example-name": "example-value"}
 
-    client = httpx.Client(transport=MockTransport(get_and_set_cookies))
+    client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies))
     response = client.get(url, cookies=cookies)
 
     assert response.status_code == 200
@@ -56,7 +55,7 @@ def test_set_cookie_with_cookiejar() -> None:
     )
     cookies.set_cookie(cookie)
 
-    client = httpx.Client(transport=MockTransport(get_and_set_cookies))
+    client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies))
     response = client.get(url, cookies=cookies)
 
     assert response.status_code == 200
@@ -91,7 +90,7 @@ def test_setting_client_cookies_to_cookiejar() -> None:
     )
     cookies.set_cookie(cookie)
 
-    client = httpx.Client(transport=MockTransport(get_and_set_cookies))
+    client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies))
     client.cookies = cookies  # type: ignore
     response = client.get(url)
 
@@ -108,7 +107,7 @@ def test_set_cookie_with_cookies_model() -> None:
     cookies = httpx.Cookies()
     cookies["example-name"] = "example-value"
 
-    client = httpx.Client(transport=MockTransport(get_and_set_cookies))
+    client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies))
     response = client.get(url, cookies=cookies)
 
     assert response.status_code == 200
@@ -118,7 +117,7 @@ def test_set_cookie_with_cookies_model() -> None:
 def test_get_cookie() -> None:
     url = "http://example.org/set_cookie"
 
-    client = httpx.Client(transport=MockTransport(get_and_set_cookies))
+    client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies))
     response = client.get(url)
 
     assert response.status_code == 200
@@ -130,7 +129,7 @@ def test_cookie_persistence() -> None:
     """
     Ensure that Client instances persist cookies between requests.
     """
-    client = httpx.Client(transport=MockTransport(get_and_set_cookies))
+    client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies))
 
     response = client.get("http://example.org/echo_cookies")
     assert response.status_code == 200
index a81f31e1e55e7b172e1a3a610bab5a997282e51a..24dc8dd70bccb07d80ee2367fcbc004c29136980 100644 (file)
@@ -1,7 +1,6 @@
 import pytest
 
 import httpx
-from tests.utils import MockTransport
 
 
 def app(request: httpx.Request) -> httpx.Response:
@@ -25,7 +24,9 @@ def test_event_hooks():
 
     event_hooks = {"request": [on_request], "response": [on_response]}
 
-    with httpx.Client(event_hooks=event_hooks, transport=MockTransport(app)) as http:
+    with httpx.Client(
+        event_hooks=event_hooks, transport=httpx.MockTransport(app)
+    ) as http:
         http.get("http://127.0.0.1:8000/", auth=("username", "password"))
 
     assert events == [
@@ -53,7 +54,9 @@ def test_event_hooks_raising_exception(server):
 
     event_hooks = {"response": [raise_on_4xx_5xx]}
 
-    with httpx.Client(event_hooks=event_hooks, transport=MockTransport(app)) as http:
+    with httpx.Client(
+        event_hooks=event_hooks, transport=httpx.MockTransport(app)
+    ) as http:
         try:
             http.get("http://127.0.0.1:8000/status/400")
         except httpx.HTTPStatusError as exc:
@@ -73,7 +76,7 @@ async def test_async_event_hooks():
     event_hooks = {"request": [on_request], "response": [on_response]}
 
     async with httpx.AsyncClient(
-        event_hooks=event_hooks, transport=MockTransport(app)
+        event_hooks=event_hooks, transport=httpx.MockTransport(app)
     ) as http:
         await http.get("http://127.0.0.1:8000/", auth=("username", "password"))
 
@@ -104,7 +107,7 @@ async def test_async_event_hooks_raising_exception():
     event_hooks = {"response": [raise_on_4xx_5xx]}
 
     async with httpx.AsyncClient(
-        event_hooks=event_hooks, transport=MockTransport(app)
+        event_hooks=event_hooks, transport=httpx.MockTransport(app)
     ) as http:
         try:
             await http.get("http://127.0.0.1:8000/status/400")
@@ -127,7 +130,9 @@ def test_event_hooks_with_redirect():
 
     event_hooks = {"request": [on_request], "response": [on_response]}
 
-    with httpx.Client(event_hooks=event_hooks, transport=MockTransport(app)) as http:
+    with httpx.Client(
+        event_hooks=event_hooks, transport=httpx.MockTransport(app)
+    ) as http:
         http.get("http://127.0.0.1:8000/redirect", auth=("username", "password"))
 
     assert events == [
@@ -166,7 +171,7 @@ async def test_async_event_hooks_with_redirect():
     event_hooks = {"request": [on_request], "response": [on_response]}
 
     async with httpx.AsyncClient(
-        event_hooks=event_hooks, transport=MockTransport(app)
+        event_hooks=event_hooks, transport=httpx.MockTransport(app)
     ) as http:
         await http.get("http://127.0.0.1:8000/redirect", auth=("username", "password"))
 
index 556cd1df141d0e4de63b046b9bd46cc711cfb5de..ba862fd52109849c0fdcc119bcfb9e5f37bb9579 100755 (executable)
@@ -3,7 +3,6 @@
 import pytest
 
 import httpx
-from tests.utils import MockTransport
 
 
 def echo_headers(request: httpx.Request) -> httpx.Response:
@@ -18,7 +17,7 @@ def test_client_header():
     url = "http://example.org/echo_headers"
     headers = {"Example-Header": "example-value"}
 
-    client = httpx.Client(transport=MockTransport(echo_headers), headers=headers)
+    client = httpx.Client(transport=httpx.MockTransport(echo_headers), headers=headers)
     response = client.get(url)
 
     assert response.status_code == 200
@@ -38,7 +37,9 @@ def test_header_merge():
     url = "http://example.org/echo_headers"
     client_headers = {"User-Agent": "python-myclient/0.2.1"}
     request_headers = {"X-Auth-Token": "FooBarBazToken"}
-    client = httpx.Client(transport=MockTransport(echo_headers), headers=client_headers)
+    client = httpx.Client(
+        transport=httpx.MockTransport(echo_headers), headers=client_headers
+    )
     response = client.get(url, headers=request_headers)
 
     assert response.status_code == 200
@@ -58,7 +59,9 @@ def test_header_merge_conflicting_headers():
     url = "http://example.org/echo_headers"
     client_headers = {"X-Auth-Token": "FooBar"}
     request_headers = {"X-Auth-Token": "BazToken"}
-    client = httpx.Client(transport=MockTransport(echo_headers), headers=client_headers)
+    client = httpx.Client(
+        transport=httpx.MockTransport(echo_headers), headers=client_headers
+    )
     response = client.get(url, headers=request_headers)
 
     assert response.status_code == 200
@@ -76,7 +79,7 @@ def test_header_merge_conflicting_headers():
 
 def test_header_update():
     url = "http://example.org/echo_headers"
-    client = httpx.Client(transport=MockTransport(echo_headers))
+    client = httpx.Client(transport=httpx.MockTransport(echo_headers))
     first_response = client.get(url)
     client.headers.update(
         {"User-Agent": "python-myclient/0.2.1", "Another-Header": "AThing"}
@@ -113,7 +116,7 @@ def test_remove_default_header():
     """
     url = "http://example.org/echo_headers"
 
-    client = httpx.Client(transport=MockTransport(echo_headers))
+    client = httpx.Client(transport=httpx.MockTransport(echo_headers))
     del client.headers["User-Agent"]
 
     response = client.get(url)
@@ -143,7 +146,7 @@ def test_host_with_auth_and_port_in_url():
     """
     url = "http://username:password@example.org:80/echo_headers"
 
-    client = httpx.Client(transport=MockTransport(echo_headers))
+    client = httpx.Client(transport=httpx.MockTransport(echo_headers))
     response = client.get(url)
 
     assert response.status_code == 200
@@ -166,7 +169,7 @@ def test_host_with_non_default_port_in_url():
     """
     url = "http://username:password@example.org:123/echo_headers"
 
-    client = httpx.Client(transport=MockTransport(echo_headers))
+    client = httpx.Client(transport=httpx.MockTransport(echo_headers))
     response = client.get(url)
 
     assert response.status_code == 200
index 6d3a9d5b5dc7875492734eeaf732626d23efe5a6..e5acb0ba20b39182b841f9549467d7b2012ac064 100644 (file)
@@ -1,5 +1,4 @@
 import httpx
-from tests.utils import MockTransport
 
 
 def hello_world(request: httpx.Request) -> httpx.Response:
@@ -28,7 +27,7 @@ def test_client_queryparams_echo():
     client_queryparams = "first=str"
     request_queryparams = {"second": "dict"}
     client = httpx.Client(
-        transport=MockTransport(hello_world), params=client_queryparams
+        transport=httpx.MockTransport(hello_world), params=client_queryparams
     )
     response = client.get(url, params=request_queryparams)
 
index 99610c771537e017da1e2481d9f5c8c93b08842f..84d371e9fa55c643dad9abd84daef0ec5c09c56f 100644 (file)
@@ -2,7 +2,6 @@ import httpcore
 import pytest
 
 import httpx
-from tests.utils import MockTransport
 
 
 def redirects(request: httpx.Request) -> httpx.Response:
@@ -116,7 +115,7 @@ def redirects(request: httpx.Request) -> httpx.Response:
 
 
 def test_redirect_301():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     response = client.post("https://example.org/redirect_301")
     assert response.status_code == httpx.codes.OK
     assert response.url == "https://example.org/"
@@ -124,7 +123,7 @@ def test_redirect_301():
 
 
 def test_redirect_302():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     response = client.post("https://example.org/redirect_302")
     assert response.status_code == httpx.codes.OK
     assert response.url == "https://example.org/"
@@ -132,7 +131,7 @@ def test_redirect_302():
 
 
 def test_redirect_303():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     response = client.get("https://example.org/redirect_303")
     assert response.status_code == httpx.codes.OK
     assert response.url == "https://example.org/"
@@ -140,7 +139,7 @@ def test_redirect_303():
 
 
 def test_next_request():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     request = client.build_request("POST", "https://example.org/redirect_303")
     response = client.send(request, allow_redirects=False)
     assert response.status_code == httpx.codes.SEE_OTHER
@@ -155,7 +154,7 @@ def test_next_request():
 
 @pytest.mark.usefixtures("async_environment")
 async def test_async_next_request():
-    async with httpx.AsyncClient(transport=MockTransport(redirects)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(redirects)) as client:
         request = client.build_request("POST", "https://example.org/redirect_303")
         response = await client.send(request, allow_redirects=False)
         assert response.status_code == httpx.codes.SEE_OTHER
@@ -172,7 +171,7 @@ def test_head_redirect():
     """
     Contrary to Requests, redirects remain enabled by default for HEAD requests.
     """
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     response = client.head("https://example.org/redirect_302")
     assert response.status_code == httpx.codes.OK
     assert response.url == "https://example.org/"
@@ -182,7 +181,7 @@ def test_head_redirect():
 
 
 def test_relative_redirect():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     response = client.get("https://example.org/relative_redirect")
     assert response.status_code == httpx.codes.OK
     assert response.url == "https://example.org/"
@@ -191,7 +190,7 @@ def test_relative_redirect():
 
 def test_malformed_redirect():
     # https://github.com/encode/httpx/issues/771
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     response = client.get("http://example.org/malformed_redirect")
     assert response.status_code == httpx.codes.OK
     assert response.url == "https://example.org:443/"
@@ -199,13 +198,13 @@ def test_malformed_redirect():
 
 
 def test_invalid_redirect():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     with pytest.raises(httpx.RemoteProtocolError):
         client.get("http://example.org/invalid_redirect")
 
 
 def test_no_scheme_redirect():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     response = client.get("https://example.org/no_scheme_redirect")
     assert response.status_code == httpx.codes.OK
     assert response.url == "https://example.org/"
@@ -213,7 +212,7 @@ def test_no_scheme_redirect():
 
 
 def test_fragment_redirect():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     response = client.get("https://example.org/relative_redirect#fragment")
     assert response.status_code == httpx.codes.OK
     assert response.url == "https://example.org/#fragment"
@@ -221,7 +220,7 @@ def test_fragment_redirect():
 
 
 def test_multiple_redirects():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     response = client.get("https://example.org/multiple_redirects?count=20")
     assert response.status_code == httpx.codes.OK
     assert response.url == "https://example.org/multiple_redirects"
@@ -234,25 +233,25 @@ def test_multiple_redirects():
 
 @pytest.mark.usefixtures("async_environment")
 async def test_async_too_many_redirects():
-    async with httpx.AsyncClient(transport=MockTransport(redirects)) as client:
+    async with httpx.AsyncClient(transport=httpx.MockTransport(redirects)) as client:
         with pytest.raises(httpx.TooManyRedirects):
             await client.get("https://example.org/multiple_redirects?count=21")
 
 
 def test_sync_too_many_redirects():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     with pytest.raises(httpx.TooManyRedirects):
         client.get("https://example.org/multiple_redirects?count=21")
 
 
 def test_redirect_loop():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     with pytest.raises(httpx.TooManyRedirects):
         client.get("https://example.org/redirect_loop")
 
 
 def test_cross_domain_redirect_with_auth_header():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     url = "https://example.com/cross_domain"
     headers = {"Authorization": "abc"}
     response = client.get(url, headers=headers)
@@ -261,7 +260,7 @@ def test_cross_domain_redirect_with_auth_header():
 
 
 def test_cross_domain_redirect_with_auth():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     url = "https://example.com/cross_domain"
     response = client.get(url, auth=("user", "pass"))
     assert response.url == "https://example.org/cross_domain_target"
@@ -269,7 +268,7 @@ def test_cross_domain_redirect_with_auth():
 
 
 def test_same_domain_redirect():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     url = "https://example.org/cross_domain"
     headers = {"Authorization": "abc"}
     response = client.get(url, headers=headers)
@@ -281,7 +280,7 @@ def test_body_redirect():
     """
     A 308 redirect should preserve the request body.
     """
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     url = "https://example.org/redirect_body"
     content = b"Example request body"
     response = client.post(url, content=content)
@@ -294,7 +293,7 @@ def test_no_body_redirect():
     """
     A 303 redirect should remove the request body.
     """
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     url = "https://example.org/redirect_no_body"
     content = b"Example request body"
     response = client.post(url, content=content)
@@ -304,7 +303,7 @@ def test_no_body_redirect():
 
 
 def test_can_stream_if_no_redirect():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     url = "https://example.org/redirect_301"
     with client.stream("GET", url, allow_redirects=False) as response:
         assert not response.is_closed
@@ -313,7 +312,7 @@ def test_can_stream_if_no_redirect():
 
 
 def test_cannot_redirect_streaming_body():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     url = "https://example.org/redirect_body"
 
     def streaming_body():
@@ -324,7 +323,7 @@ def test_cannot_redirect_streaming_body():
 
 
 def test_cross_subdomain_redirect():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     url = "https://example.com/cross_subdomain"
     response = client.get(url)
     assert response.url == "https://www.example.org/cross_subdomain"
@@ -364,7 +363,7 @@ def cookie_sessions(request: httpx.Request) -> httpx.Response:
 
 
 def test_redirect_cookie_behavior():
-    client = httpx.Client(transport=MockTransport(cookie_sessions))
+    client = httpx.Client(transport=httpx.MockTransport(cookie_sessions))
 
     # The client is not logged in.
     response = client.get("https://example.com/")
@@ -393,7 +392,7 @@ def test_redirect_cookie_behavior():
 
 
 def test_redirect_custom_scheme():
-    client = httpx.Client(transport=MockTransport(redirects))
+    client = httpx.Client(transport=httpx.MockTransport(redirects))
     with pytest.raises(httpx.UnsupportedProtocol) as e:
         client.post("https://example.org/redirect_custom_scheme")
     assert str(e.value) == "Scheme 'market' not supported."
index 8852c9f905269287612e791252c8a76ab1df5d81..94813932a8ea054b14c1c83c94408236cfe12b8d 100644 (file)
@@ -9,7 +9,6 @@ import pytest
 import httpx
 from httpx._content import encode_request
 from httpx._utils import format_form_param
-from tests.utils import MockTransport
 
 
 def echo_request_content(request: httpx.Request) -> httpx.Response:
@@ -18,7 +17,7 @@ def echo_request_content(request: httpx.Request) -> httpx.Response:
 
 @pytest.mark.parametrize(("value,output"), (("abc", b"abc"), (b"abc", b"abc")))
 def test_multipart(value, output):
-    client = httpx.Client(transport=MockTransport(echo_request_content))
+    client = httpx.Client(transport=httpx.MockTransport(echo_request_content))
 
     # Test with a single-value 'data' argument, and a plain file 'files' argument.
     data = {"text": value}
@@ -44,7 +43,7 @@ def test_multipart(value, output):
 
 @pytest.mark.parametrize(("key"), (b"abc", 1, 2.3, None))
 def test_multipart_invalid_key(key):
-    client = httpx.Client(transport=MockTransport(echo_request_content))
+    client = httpx.Client(transport=httpx.MockTransport(echo_request_content))
 
     data = {key: "abc"}
     files = {"file": io.BytesIO(b"<file content>")}
@@ -60,7 +59,7 @@ def test_multipart_invalid_key(key):
 
 @pytest.mark.parametrize(("value"), (1, 2.3, None, [None, "abc"], {None: "abc"}))
 def test_multipart_invalid_value(value):
-    client = httpx.Client(transport=MockTransport(echo_request_content))
+    client = httpx.Client(transport=httpx.MockTransport(echo_request_content))
 
     data = {"text": value}
     files = {"file": io.BytesIO(b"<file content>")}
@@ -70,7 +69,7 @@ def test_multipart_invalid_value(value):
 
 
 def test_multipart_file_tuple():
-    client = httpx.Client(transport=MockTransport(echo_request_content))
+    client = httpx.Client(transport=httpx.MockTransport(echo_request_content))
 
     # Test with a list of values 'data' argument,
     #     and a tuple style 'files' argument.
index 75157dee7ef4816718ddf42039bf563b09550c56..e2636a535cfb5ac2dcc96fb3cfd974ae7ada134a 100644 (file)
@@ -1,11 +1,7 @@
 import contextlib
 import logging
 import os
-from typing import Callable, List, Optional, Tuple
 
-import httpcore
-
-import httpx
 from httpx import _utils
 
 
@@ -22,54 +18,3 @@ def override_log_level(log_level: str):
     finally:
         # Reset the logger so we don't have verbose output in all unit tests
         logging.getLogger("httpx").handlers = []
-
-
-class MockTransport(httpcore.SyncHTTPTransport, httpcore.AsyncHTTPTransport):
-    def __init__(self, handler: Callable) -> None:
-        self.handler = handler
-
-    def request(
-        self,
-        method: bytes,
-        url: Tuple[bytes, bytes, Optional[int], bytes],
-        headers: List[Tuple[bytes, bytes]] = None,
-        stream: httpcore.SyncByteStream = None,
-        ext: dict = None,
-    ) -> Tuple[int, List[Tuple[bytes, bytes]], httpcore.SyncByteStream, dict]:
-        request = httpx.Request(
-            method=method,
-            url=url,
-            headers=headers,
-            stream=stream,
-        )
-        request.read()
-        response = self.handler(request)
-        return (
-            response.status_code,
-            response.headers.raw,
-            response.stream,
-            response.ext,
-        )
-
-    async def arequest(
-        self,
-        method: bytes,
-        url: Tuple[bytes, bytes, Optional[int], bytes],
-        headers: List[Tuple[bytes, bytes]] = None,
-        stream: httpcore.AsyncByteStream = None,
-        ext: dict = None,
-    ) -> Tuple[int, List[Tuple[bytes, bytes]], httpcore.AsyncByteStream, dict]:
-        request = httpx.Request(
-            method=method,
-            url=url,
-            headers=headers,
-            stream=stream,
-        )
-        await request.aread()
-        response = self.handler(request)
-        return (
-            response.status_code,
-            response.headers.raw,
-            response.stream,
-            response.ext,
-        )