import httpx
-async def test_get(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_get(server):
url = server.url
async with httpx.Client() as client:
response = await client.get(url)
assert response.elapsed > timedelta(seconds=0)
-async def test_build_request(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_build_request(server):
url = server.url.copy_with(path="/echo_headers")
headers = {"Custom-header": "value"}
async with httpx.Client() as client:
assert response.json()["Custom-header"] == "value"
-@pytest.mark.asyncio
-async def test_get_no_backend(server):
- """
- Verify that the client is capable of making a simple request if not given a backend.
- """
- url = server.url
- async with httpx.Client() as client:
- response = await client.get(url)
- assert response.status_code == 200
- assert response.text == "Hello, world!"
- assert response.http_version == "HTTP/1.1"
- assert response.headers
- assert repr(response) == "<Response [200 OK]>"
-
-
-async def test_post(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_post(server):
url = server.url
async with httpx.Client() as client:
response = await client.post(url, data=b"Hello, world!")
assert response.status_code == 200
-async def test_post_json(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_post_json(server):
url = server.url
async with httpx.Client() as client:
response = await client.post(url, json={"text": "Hello, world!"})
assert response.status_code == 200
-async def test_stream_response(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_stream_response(server):
async with httpx.Client() as client:
async with client.stream("GET", server.url) as response:
body = await response.read()
assert response.content == b"Hello, world!"
-async def test_access_content_stream_response(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_access_content_stream_response(server):
async with httpx.Client() as client:
async with client.stream("GET", server.url) as response:
pass
response.content
-async def test_stream_request(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_stream_request(server):
async def hello_world():
yield b"Hello, "
yield b"world!"
assert response.status_code == 200
-async def test_raise_for_status(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_raise_for_status(server):
async with httpx.Client() as client:
for status_code in (200, 400, 404, 500, 505):
response = await client.request(
assert response.raise_for_status() is None
-async def test_options(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_options(server):
async with httpx.Client() as client:
response = await client.options(server.url)
assert response.status_code == 200
assert response.text == "Hello, world!"
-async def test_head(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_head(server):
async with httpx.Client() as client:
response = await client.head(server.url)
assert response.status_code == 200
assert response.text == ""
-async def test_put(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_put(server):
async with httpx.Client() as client:
response = await client.put(server.url, data=b"Hello, world!")
assert response.status_code == 200
-async def test_patch(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_patch(server):
async with httpx.Client() as client:
response = await client.patch(server.url, data=b"Hello, world!")
assert response.status_code == 200
-async def test_delete(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_delete(server):
async with httpx.Client() as client:
response = await client.delete(server.url)
assert response.status_code == 200
assert response.text == "Hello, world!"
-async def test_100_continue(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_100_continue(server):
headers = {"Expect": "100-continue"}
data = b"Echo request body"
assert response.content == data
-async def test_uds(uds_server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_uds(uds_server):
url = uds_server.url
uds = uds_server.config.uds
assert uds is not None
assert response.encoding == "iso-8859-1"
-@pytest.mark.parametrize(
- "backend",
- [
- pytest.param("asyncio", marks=pytest.mark.asyncio),
- pytest.param("trio", marks=pytest.mark.trio),
- ],
-)
-async def test_explicit_backend(server, backend):
- async with httpx.Client(backend=backend) as client:
+async def test_explicit_backend(server, async_environment):
+ async with httpx.Client(backend=async_environment) as client:
response = await client.get(server.url)
assert response.status_code == 200
assert response.text == "Hello, world!"
return Response(codes.OK, content=b"Hello, world!", request=request)
-async def test_no_redirect(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_no_redirect():
client = Client(dispatch=MockDispatch())
url = "https://example.com/no_redirect"
response = await client.get(url)
await response.next()
-async def test_redirect_301(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_redirect_301():
client = Client(dispatch=MockDispatch())
response = await client.post("https://example.org/redirect_301")
assert response.status_code == codes.OK
assert len(response.history) == 1
-async def test_redirect_302(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_redirect_302():
client = Client(dispatch=MockDispatch())
response = await client.post("https://example.org/redirect_302")
assert response.status_code == codes.OK
assert len(response.history) == 1
-async def test_redirect_303(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_redirect_303():
client = Client(dispatch=MockDispatch())
response = await client.get("https://example.org/redirect_303")
assert response.status_code == codes.OK
assert len(response.history) == 1
-async def test_disallow_redirects(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_disallow_redirects():
client = Client(dispatch=MockDispatch())
response = await client.post(
"https://example.org/redirect_303", allow_redirects=False
assert len(response.history) == 1
-async def test_relative_redirect(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_relative_redirect():
client = Client(dispatch=MockDispatch())
response = await client.get("https://example.org/relative_redirect")
assert response.status_code == codes.OK
assert len(response.history) == 1
-async def test_no_scheme_redirect(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_no_scheme_redirect():
client = Client(dispatch=MockDispatch())
response = await client.get("https://example.org/no_scheme_redirect")
assert response.status_code == codes.OK
assert len(response.history) == 1
-async def test_fragment_redirect(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_fragment_redirect():
client = Client(dispatch=MockDispatch())
response = await client.get("https://example.org/relative_redirect#fragment")
assert response.status_code == codes.OK
assert len(response.history) == 1
-async def test_multiple_redirects(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_multiple_redirects():
client = Client(dispatch=MockDispatch())
response = await client.get("https://example.org/multiple_redirects?count=20")
assert response.status_code == codes.OK
assert len(response.history[1].history) == 1
-async def test_too_many_redirects(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_too_many_redirects():
client = Client(dispatch=MockDispatch())
with pytest.raises(TooManyRedirects):
await client.get("https://example.org/multiple_redirects?count=21")
-async def test_too_many_redirects_calling_next(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_too_many_redirects_calling_next():
client = Client(dispatch=MockDispatch())
url = "https://example.org/multiple_redirects?count=21"
response = await client.get(url, allow_redirects=False)
response = await response.next()
-async def test_redirect_loop(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_redirect_loop():
client = Client(dispatch=MockDispatch())
with pytest.raises(RedirectLoop):
await client.get("https://example.org/redirect_loop")
-async def test_redirect_loop_calling_next(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_redirect_loop_calling_next():
client = Client(dispatch=MockDispatch())
url = "https://example.org/redirect_loop"
response = await client.get(url, allow_redirects=False)
response = await response.next()
-async def test_cross_domain_redirect(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_cross_domain_redirect():
client = Client(dispatch=MockDispatch())
url = "https://example.com/cross_domain"
headers = {"Authorization": "abc"}
assert "authorization" not in response.json()["headers"]
-async def test_same_domain_redirect(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_same_domain_redirect():
client = Client(dispatch=MockDispatch())
url = "https://example.org/cross_domain"
headers = {"Authorization": "abc"}
assert response.json()["headers"]["authorization"] == "abc"
-async def test_body_redirect(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_body_redirect():
"""
A 308 redirect should preserve the request body.
"""
assert "content-length" in response.json()["headers"]
-async def test_no_body_redirect(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_no_body_redirect():
"""
A 303 redirect should remove the request body.
"""
assert "content-length" not in response.json()["headers"]
-async def test_cannot_redirect_streaming_body(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_cannot_redirect_streaming_body():
client = Client(dispatch=MockDispatch())
url = "https://example.org/redirect_body"
await client.post(url, data=streaming_body())
-async def test_cross_subdomain_redirect(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_cross_subdomain_redirect():
client = Client(dispatch=MockDispatch())
url = "https://example.com/cross_subdomain"
response = await client.get(url)
return Response(status_code, headers=headers, request=request)
-async def test_redirect_cookie_behavior(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_redirect_cookie_behavior():
client = Client(dispatch=MockCookieDispatch())
# The client is not logged in.
}
+@pytest.fixture(
+ params=[
+ pytest.param("asyncio", marks=pytest.mark.asyncio),
+ pytest.param("trio", marks=pytest.mark.trio),
+ ]
+)
+def async_environment(request: typing.Any) -> str:
+ """
+ Mark a test function to be run on both asyncio and trio.
+
+ Equivalent to having a pair of tests, each respectively marked with
+ '@pytest.mark.asyncio' and '@pytest.mark.trio'.
+
+ Intended usage:
+
+ ```
+ @pytest.mark.usefixtures("async_environment")
+ async def my_async_test():
+ ...
+ ```
+ """
+ return request.param
+
+
@pytest.fixture(scope="function", autouse=True)
def clean_environ() -> typing.Dict[str, typing.Any]:
"""Keeps os.environ clean for every test without having to mock os.environ"""
os.environ.update(original_environ)
-@pytest.fixture(
- params=[
- # pytest uses the marks to set up the specified async environment and run
- # 'async def' test functions. The "auto" backend should then auto-detect
- # the environment it's running in.
- # Passing the backend explicitly, e.g. `backend="asyncio"`,
- # is tested separately.
- pytest.param("auto", marks=pytest.mark.asyncio),
- pytest.param("auto", marks=pytest.mark.trio),
- ]
-)
-def backend(request):
- return request.param
-
-
async def app(scope, receive, send):
assert scope["type"] == "http"
if scope["path"].startswith("/slow_response"):
@pytest.fixture
-def restart(backend):
+def restart():
"""Restart the running server from an async test function.
This fixture deals with possible differences between the environment of the
test function and that of the server.
"""
asyncio_backend = AsyncioBackend()
- backend_implementation = lookup_backend(backend)
async def restart(server):
- await backend_implementation.run_in_threadpool(
- asyncio_backend.run, server.restart
- )
+ backend = lookup_backend()
+ await backend.run_in_threadpool(asyncio_backend.run, server.restart)
return restart
+import pytest
+
import httpx
from httpx.dispatch.connection_pool import ConnectionPool
-async def test_keepalive_connections(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_keepalive_connections(server):
"""
Connections should default to staying in a keep-alive state.
"""
assert len(http.keepalive_connections) == 1
-async def test_keepalive_timeout(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_keepalive_timeout(server):
"""
Keep-alive connections should timeout.
"""
assert len(http.keepalive_connections) == 0
-async def test_differing_connection_keys(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_differing_connection_keys(server):
"""
Connections to differing connection keys should result in multiple connections.
"""
assert len(http.keepalive_connections) == 2
-async def test_soft_limit(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_soft_limit(server):
"""
The soft_limit config should limit the maximum number of keep-alive connections.
"""
assert len(http.keepalive_connections) == 1
-async def test_streaming_response_holds_connection(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_streaming_response_holds_connection(server):
"""
A streaming request should hold the connection open until the response is read.
"""
assert len(http.keepalive_connections) == 1
-async def test_multiple_concurrent_connections(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_multiple_concurrent_connections(server):
"""
Multiple conncurrent requests should open multiple conncurrent connections.
"""
assert len(http.keepalive_connections) == 2
-async def test_close_connections(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_close_connections(server):
"""
Using a `Connection: close` header should close the connection.
"""
assert len(http.keepalive_connections) == 0
-async def test_standard_response_close(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_standard_response_close(server):
"""
A standard close should keep the connection open.
"""
assert len(http.keepalive_connections) == 1
-async def test_premature_response_close(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_premature_response_close(server):
"""
A premature close should close the connection.
"""
assert len(http.keepalive_connections) == 0
-async def test_keepalive_connection_closed_by_server_is_reestablished(
- server, restart, backend
-):
+@pytest.mark.usefixtures("async_environment")
+async def test_keepalive_connection_closed_by_server_is_reestablished(server, restart):
"""
Upon keep-alive connection closed by remote a new connection
should be reestablished.
assert len(http.keepalive_connections) == 1
+@pytest.mark.usefixtures("async_environment")
async def test_keepalive_http2_connection_closed_by_server_is_reestablished(
- server, restart, backend
+ server, restart
):
"""
Upon keep-alive connection closed by remote a new connection
assert len(http.keepalive_connections) == 1
-async def test_connection_closed_free_semaphore_on_acquire(server, restart, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_connection_closed_free_semaphore_on_acquire(server, restart):
"""
Verify that max_connections semaphore is released
properly on a disconnected connection.
from httpx.dispatch.connection import HTTPConnection
-async def test_get(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_get(server):
async with HTTPConnection(origin=server.url) as conn:
response = await conn.request("GET", server.url)
await response.read()
assert response.content == b"Hello, world!"
-async def test_post(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_post(server):
async with HTTPConnection(origin=server.url) as conn:
response = await conn.request("GET", server.url, data=b"Hello, world!")
assert response.status_code == 200
-async def test_premature_close(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_premature_close(server):
with pytest.raises(httpx.ConnectionClosed):
async with HTTPConnection(origin=server.url) as conn:
response = await conn.request(
await response.read()
-async def test_https_get_with_ssl_defaults(https_server, ca_cert_pem_file, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_https_get_with_ssl_defaults(https_server, ca_cert_pem_file):
"""
An HTTPS request, with default SSL configuration set on the client.
"""
assert response.content == b"Hello, world!"
-async def test_https_get_with_sll_overrides(https_server, ca_cert_pem_file, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_https_get_with_sll_overrides(https_server, ca_cert_pem_file):
"""
An HTTPS request, with SSL configuration set on the request.
"""
assert json.loads(response_2.content) == {"method": "GET", "path": "/2", "body": ""}
-async def test_http2_settings_in_handshake(backend):
+@pytest.mark.asyncio
+async def test_http2_settings_in_handshake():
backend = MockHTTP2Backend(app=app)
async with Client(backend=backend, http2=True) as client:
assert changed_setting.new_value == expected_settings[setting_code]
-async def test_http2_live_request(backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_http2_live_request():
async with Client(http2=True) as client:
try:
resp = await client.get("https://nghttp2.org/httpbin/anything")
from .utils import MockRawSocketBackend
-async def test_proxy_tunnel_success(backend):
+@pytest.mark.asyncio
+async def test_proxy_tunnel_success():
raw_io = MockRawSocketBackend(
data_to_send=(
[
assert recv[2].startswith(b"GET / HTTP/1.1\r\nhost: example.com\r\n")
+@pytest.mark.asyncio
@pytest.mark.parametrize("status_code", [300, 304, 308, 401, 500])
-async def test_proxy_tunnel_non_2xx_response(backend, status_code):
+async def test_proxy_tunnel_non_2xx_response(status_code):
raw_io = MockRawSocketBackend(
data_to_send=(
[
)
-async def test_proxy_tunnel_start_tls(backend):
+@pytest.mark.asyncio
+async def test_proxy_tunnel_start_tls():
raw_io = MockRawSocketBackend(
data_to_send=(
[
assert recv[4].startswith(b"GET /target HTTP/1.1\r\nhost: example.com\r\n")
+@pytest.mark.asyncio
@pytest.mark.parametrize("proxy_mode", ["FORWARD_ONLY", "DEFAULT"])
-async def test_proxy_forwarding(backend, proxy_mode):
+async def test_proxy_forwarding(proxy_mode):
raw_io = MockRawSocketBackend(
data_to_send=(
[
return response
-async def test_start_tls_on_tcp_socket_stream(https_server, backend):
- backend = lookup_backend(backend)
+@pytest.mark.usefixtures("async_environment")
+async def test_start_tls_on_tcp_socket_stream(https_server):
+ backend = lookup_backend()
ctx = SSLConfig().load_ssl_context_no_verify()
timeout = Timeout(5)
await stream.close()
-async def test_start_tls_on_uds_socket_stream(https_uds_server, backend):
- backend = lookup_backend(backend)
+@pytest.mark.usefixtures("async_environment")
+async def test_start_tls_on_uds_socket_stream(https_uds_server):
+ backend = lookup_backend()
ctx = SSLConfig().load_ssl_context_no_verify()
timeout = Timeout(5)
await stream.close()
-async def test_concurrent_read(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_concurrent_read(server):
"""
Regression test for: https://github.com/encode/httpx/issues/527
"""
- backend = lookup_backend(backend)
+ backend = lookup_backend()
stream = await backend.open_tcp_stream(
server.url.host, server.url.port, ssl_context=None, timeout=Timeout(5)
)
import httpx
-async def test_read_timeout(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_read_timeout(server):
timeout = httpx.Timeout(read_timeout=1e-6)
async with httpx.Client(timeout=timeout) as client:
await client.get(server.url.copy_with(path="/slow_response"))
-async def test_write_timeout(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_write_timeout(server):
timeout = httpx.Timeout(write_timeout=1e-6)
async with httpx.Client(timeout=timeout) as client:
await client.put(server.url.copy_with(path="/slow_response"), data=data)
-async def test_connect_timeout(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_connect_timeout(server):
timeout = httpx.Timeout(connect_timeout=1e-6)
async with httpx.Client(timeout=timeout) as client:
await client.get("http://10.255.255.1/")
-async def test_pool_timeout(server, backend):
+@pytest.mark.usefixtures("async_environment")
+async def test_pool_timeout(server):
pool_limits = httpx.PoolLimits(hard_limit=1)
timeout = httpx.Timeout(pool_timeout=1e-4)