diff --git a/docs/testclient.md b/docs/testclient.md index f37858401..a1861efec 100644 --- a/docs/testclient.md +++ b/docs/testclient.md @@ -33,18 +33,25 @@ case you should use `client = TestClient(app, raise_server_exceptions=False)`. ### Selecting the Async backend -`TestClient.async_backend` is a dictionary which allows you to set the options -for the backend used to run tests. These options are passed to -`anyio.start_blocking_portal()`. See the [anyio documentation](https://anyio.readthedocs.io/en/stable/basics.html#backend-options) -for more information about backend options. By default, `asyncio` is used. +`TestClient` takes arguments `backend` (a string) and `backend_options` (a dictionary). +These options are passed to `anyio.start_blocking_portal()`. See the [anyio documentation](https://anyio.readthedocs.io/en/stable/basics.html#backend-options) +for more information about the accepted backend options. +By default, `asyncio` is used with default options. -To run `Trio`, set `async_backend["backend"] = "trio"`, for example: +To run `Trio`, pass `backend="trio"`. For example: ```python def test_app() - client = TestClient(app) - client.async_backend["backend"] = "trio" - ... + with TestClient(app, backend="trio") as client: + ... +``` + +To run `asyncio` with `uvloop`, pass `backend_options={"use_uvloop": True}`. For example: + +```python +def test_app() + with TestClient(app, backend_options={"use_uvloop": True}) as client: + ... ``` ### Testing WebSocket sessions diff --git a/starlette/testclient.py b/starlette/testclient.py index 6675f4971..7201809e2 100644 --- a/starlette/testclient.py +++ b/starlette/testclient.py @@ -381,13 +381,11 @@ def receive_json(self, mode: str = "text") -> typing.Any: class TestClient(requests.Session): __test__ = False # For pytest to not discover this up. - - #: These options are passed to `anyio.start_blocking_portal()` + #: These are the default options for the constructor arguments async_backend: typing.Dict[str, typing.Any] = { "backend": "asyncio", "backend_options": {}, } - task: "Future[None]" def __init__( @@ -396,8 +394,14 @@ def __init__( base_url: str = "http://testserver", raise_server_exceptions: bool = True, root_path: str = "", + backend: typing.Optional[str] = None, + backend_options: typing.Optional[typing.Dict[str, typing.Any]] = None, ) -> None: super().__init__() + self.async_backend = { + "backend": backend or self.async_backend["backend"], + "backend_options": backend_options or self.async_backend["backend_options"], + } if _is_asgi3(app): app = typing.cast(ASGI3App, app) asgi_app = app diff --git a/tests/test_testclient.py b/tests/test_testclient.py index 86f36e172..44e3320a4 100644 --- a/tests/test_testclient.py +++ b/tests/test_testclient.py @@ -132,3 +132,22 @@ async def asgi(receive, send): with client.websocket_connect("/") as websocket: data = websocket.receive_json() assert data == {"message": "test"} + + +def test_backend_name(request): + """ + Test that the tests are defaulting to the correct backend and that a new + instance of TestClient can be created using different backend options. + """ + # client created using monkeypatched async_backend + client1 = TestClient(mock_service) + if "trio" in request.keywords: + client2 = TestClient(mock_service, backend="asyncio") + assert client1.async_backend["backend"] == "trio" + assert client2.async_backend["backend"] == "asyncio" + elif "asyncio" in request.keywords: + client2 = TestClient(mock_service, backend="trio") + assert client1.async_backend["backend"] == "asyncio" + assert client2.async_backend["backend"] == "trio" + else: + pytest.fail("Unknown backend") # pragma: nocover