async def test_pass_through(client, args, kwargs, expected): async with MockTransport() as respx_mock: request = respx_mock.add(*args, **kwargs) with mock.patch( "asyncio.open_connection", side_effect=ConnectionRefusedError("test request blocked"), ) as open_connection: with pytest.raises(httpx.NetworkError): await client.get("https://example.org/") assert open_connection.called is True assert request.called is True assert request.is_pass_through is expected with MockTransport() as respx_mock: request = respx_mock.add(*args, **kwargs) with mock.patch( "socket.socket.connect", side_effect=socket.error("test request blocked")) as connect: with pytest.raises(httpx.NetworkError): httpx.get("https://example.org/") assert connect.called is True assert request.called is True assert request.is_pass_through is expected
async def test_assert_all_mocked(client, assert_all_mocked, raises): with raises: with MockTransport(assert_all_mocked=assert_all_mocked) as respx_mock: response = httpx.get("https://foo.bar/") assert respx_mock.stats.call_count == 1 assert response.status_code == 200 with raises: async with MockTransport(assert_all_mocked=assert_all_mocked) as respx_mock: response = await client.get("https://foo.bar/") assert respx_mock.stats.call_count == 1 assert response.status_code == 200 assert respx_mock.stats.call_count == 0
async def test_callable_content(client): async with MockTransport() as respx_mock: url_pattern = re.compile(r"https://foo.bar/(?P<slug>\w+)/") def content_callback(request, slug): request.read( ) # TODO: Make this not needed, might affect pass-through content = jsonlib.loads(request.content) return f"hello {slug}{content['x']}" request = respx_mock.post(url_pattern, content=content_callback) async_response = await client.post("https://foo.bar/world/", json={"x": "."}) assert request.called is True assert async_response.status_code == 200 assert async_response.text == "hello world." assert request.calls[-1][0].content == b'{"x": "."}' respx_mock.reset() sync_response = httpx.post("https://foo.bar/jonas/", json={"x": "!"}) assert request.called is True assert sync_response.status_code == 200 assert sync_response.text == "hello jonas!" assert request.calls[-1][0].content == b'{"x": "!"}'
async def test_url_match(client, url, pattern): async with MockTransport(assert_all_mocked=False) as respx_mock: request = respx_mock.get(pattern, content="baz") response = await client.get(url) assert request.called is True assert response.status_code == 200 assert response.text == "baz"
async def test_headers(client, headers, content_type, expected): async with MockTransport() as respx_mock: url = "https://foo.bar/" request = respx_mock.get(url, content_type=content_type, headers=headers) response = await client.get(url) assert request.called is True assert response.headers == httpx.Headers(expected)
async def test_text_encoding(client, content, expected): async with MockTransport() as respx_mock: url = "https://foo.bar/" request = respx_mock.post(url, content=content) response = await client.post(url) assert request.called is True assert response.text == expected
async def test_httpcore_request(): async with MockTransport() as transport: transport.add("GET", "https://foo.bar/", content="foobar") with httpcore.SyncConnectionPool() as http: (http_version, status_code, reason_phrase, headers, stream,) = http.request( method=b"GET", url=(b"https", b"foo.bar", 443, b"/"), ) body = b"".join([chunk for chunk in stream]) stream.close() assert body == b"foobar" async with httpcore.AsyncConnectionPool() as http: ( http_version, status_code, reason_phrase, headers, stream, ) = await http.request( method=b"GET", url=(b"https", b"foo.bar", 443, b"/"), ) body = b"".join([chunk async for chunk in stream]) await stream.aclose() assert body == b"foobar"
async def test_request_callback(client): def callback(request, response): request.read() if request.url.host == "foo.bar" and request.content == b'{"foo": "bar"}': response.headers["X-Foo"] = "bar" response.content = lambda request, name: f"hello {name}" response.context["name"] = "lundberg" response.http_version = "HTTP/2" return response async with MockTransport(assert_all_called=False) as respx_mock: request = respx_mock.add(callback, status_code=202, headers={"X-Ham": "spam"}) response = await client.post("https://foo.bar/", json={"foo": "bar"}) assert request.called is True assert request.pass_through is None assert response.status_code == 202 assert response.http_version == "HTTP/2" assert response.headers == httpx.Headers({ "Content-Type": "text/plain; charset=utf-8", "Content-Length": "14", "X-Ham": "spam", "X-Foo": "bar", }) assert response.text == "hello lundberg" with pytest.raises(ValueError): respx_mock.add(lambda req, res: "invalid") await client.get("https://ham.spam/")
async def test_status_code(client): async with MockTransport() as respx_mock: url = "https://foo.bar/" request = respx_mock.get(url, status_code=404) response = await client.get(url) assert request.called is True assert response.status_code == 404
async def test_alias(): async with MockTransport(assert_all_called=False) as respx_mock: url = "https://foo.bar/" request = respx_mock.get(url, alias="foobar") assert "foobar" not in respx.aliases assert "foobar" in respx_mock.aliases assert respx_mock.aliases["foobar"].url == request.url assert respx_mock["foobar"].url == request.url
async def test_text_content(client, content, expected): async with MockTransport() as respx_mock: url = "https://foo.bar/" content_type = "text/plain; charset=utf-8" # TODO: Remove once respected request = respx_mock.post(url, content=content, content_type=content_type) response = await client.post(url) assert request.called is True assert response.text == expected
async def test_add(client, method_str, client_method_attr): url = "https://example.org/" content = {"spam": "lots", "ham": "no, only spam"} async with MockTransport() as respx_mock: request = respx_mock.add(method_str, url, content=content) response = await getattr(client, client_method_attr)(url) assert request.called is True assert response.json() == content
async def test_alias(): async with MockTransport(assert_all_called=False) as respx_mock: request = respx_mock.get("https://foo.bar/", content="foo bar", name="foobar") assert "foobar" not in respx.aliases assert "foobar" in respx_mock.aliases assert respx_mock.aliases["foobar"] is request assert respx_mock["foobar"] is request
async def test_raising_content(client): async with MockTransport() as respx_mock: url = "https://foo.bar/" request = respx_mock.get(url, content=httpx.ConnectTimeout()) with pytest.raises(httpx.ConnectTimeout): await client.get(url) assert request.called is True _request, _response = request.calls[-1] assert _request is not None assert _response is None
async def test_assert_all_called(client, assert_all_called, do_post, raises): with raises: async with MockTransport(assert_all_called=assert_all_called) as respx_mock: request1 = respx_mock.get("https://foo.bar/1/", status_code=404) request2 = respx_mock.post("https://foo.bar/", status_code=201) await client.get("https://foo.bar/1/") if do_post: await client.post("https://foo.bar/") assert request1.called is True assert request2.called is do_post
async def test_json_content(client, content, headers, expected_headers): async with MockTransport() as respx_mock: url = "https://foo.bar/" request = respx_mock.get(url, content=content, headers=headers) async_response = await client.get(url) assert request.called is True assert async_response.headers == httpx.Headers(expected_headers) assert async_response.json() == content respx_mock.reset() sync_response = httpx.get(url) assert request.called is True assert sync_response.headers == httpx.Headers(expected_headers) assert sync_response.json() == content
async def test_callable_content(client): async with MockTransport() as respx_mock: url_pattern = re.compile(r"https://foo.bar/(?P<slug>\w+)/") content = lambda request, slug: f"hello {slug}" request = respx_mock.get(url_pattern, content=content) async_response = await client.get("https://foo.bar/world/") assert request.called is True assert async_response.status_code == 200 assert async_response.text == "hello world" respx_mock.reset() sync_response = httpx.get("https://foo.bar/world/") assert request.called is True assert sync_response.status_code == 200 assert sync_response.text == "hello world"
async def test_httpx_exception_handling(client): # pragma: no cover async with MockTransport() as respx_mock: with mock.patch( "httpx._client.AsyncClient.dispatcher_for_url", side_effect=ValueError("mock"), ): url = "https://foo.bar/" request = respx_mock.get(url) with pytest.raises(ValueError): await client.get(url) assert request.called is True assert respx_mock.stats.call_count == 1 _request, _response = respx_mock.calls[-1] assert _request is not None assert _response is None
async def test_content_variants(client, key, value, expected_content_type): async with MockTransport() as respx_mock: url = "https://foo.bar/" request = respx_mock.get(url, **{key: value}) async_response = await client.get(url) assert request.called is True assert async_response.headers.get( "Content-Type") == expected_content_type assert async_response.content is not None respx_mock.reset() sync_response = httpx.get(url) assert request.called is True assert sync_response.headers.get( "Content-Type") == expected_content_type assert sync_response.content is not None
async def test_repeated_pattern(client): async with MockTransport() as respx_mock: url = "https://foo/bar/baz/" one = respx_mock.post(url, status_code=201) two = respx_mock.post(url, status_code=409) assert one is two assert len(one._responses) == 2 response1 = await client.post(url, json={}) response2 = await client.post(url, json={}) response3 = await client.post(url, json={}) assert response1.status_code == 201 assert response2.status_code == 409 assert response3.status_code == 409 assert respx_mock.calls.call_count == 3 assert one.called is True assert one.call_count == 3 statuses = [call.response.status_code for call in one.calls] assert statuses == [201, 409, 409]
async def test_raising_content(client): async with MockTransport() as respx_mock: url = "https://foo.bar/" request = respx_mock.get(url, content=httpx.ConnectTimeout("X-P", request=None)) with pytest.raises(httpx.ConnectTimeout): await client.get(url) assert request.called is True _request, _response = request.calls[-1] assert _request is not None assert _response is None # Test httpx exception class get instantiated route = respx_mock.get(url).side_effect(httpx.ConnectError) with pytest.raises(httpx.ConnectError): await client.get(url) assert route.called is True assert route.calls.last.request is not None assert route.calls.last.response is None
async def test_repeated_pattern(client): async with MockTransport() as respx_mock: url = "https://foo/bar/baz/" one = respx_mock.post(url, status_code=201) two = respx_mock.post(url, status_code=409) response1 = await client.post(url, json={}) response2 = await client.post(url, json={}) response3 = await client.post(url, json={}) assert response1.status_code == 201 assert response2.status_code == 409 assert response3.status_code == 409 assert respx_mock.stats.call_count == 3 assert one.called is True assert one.call_count == 1 statuses = [response.status_code for _, response in one.calls] assert statuses == [201] assert two.called is True assert two.call_count == 2 statuses = [response.status_code for _, response in two.calls] assert statuses == [409, 409]
async def test_invalid_url_pattern(): async with MockTransport() as respx_mock: with pytest.raises(AssertionError): respx_mock.get(["invalid"])