# Test Clients

There are three different test clients available to you, each of them presents different capabilities.

## Regular sync client: `SanicTestClient`

The `SanicTestClient` runs an actual version of the Sanic Server on your local network to run its tests. Each time it calls an endpoint it will spin up a version of the application and bind it to a socket on the host OS. Then, it will use `httpx` to make calls directly to that application.

This is the typical way that Sanic applications are tested.

.. column::

    Once installing Sanic Testing, the regular `SanicTestClient` can be used without further setup. This is because Sanic does the leg work for you under the hood. 

.. column::

    ```python
    app.test_client.get("/path/to/endpoint")
    ```

.. column::

    However, you may find it desirable to instantiate the client yourself.

.. column::

    ```python
    from sanic_testing.testing import SanicTestClient

    test_client = SanicTestClient(app)
    test_client.get("/path/to/endpoint")
    ```

.. column::

    A third option for starting the test client is to use the `TestManager`. This is a convenience object that sets up both the `SanicTestClient` and the `SanicASGITestClient`.

.. column::

    ```python
    from sanic_testing import TestManager

    mgr = TestManager(app)
    app.test_client.get("/path/to/endpoint")
    # or
    mgr.test_client.get("/path/to/endpoint")
    ```

You can make a request by using one of the following methods

- `SanicTestClient.get`
- `SanicTestClient.post`
- `SanicTestClient.put`
- `SanicTestClient.patch`
- `SanicTestClient.delete`
- `SanicTestClient.options`
- `SanicTestClient.head`
- `SanicTestClient.websocket`
- `SanicTestClient.request`

You can use these methods *almost* identically as you would when using `httpx`. Any argument that you would pass to `httpx` will be accepted, **with one caveat**: If you are using `test_client.request` and want to manually specify the HTTP method, you should use: `http_method`:

```python
test_client.request("/path/to/endpoint", http_method="get")
```

## ASGI async client: `SanicASGITestClient`

Unlike the `SanicTestClient` that spins up a server on every request, the `SanicASGITestClient` does not. Instead it makes use of the `httpx` library to execute Sanic as an ASGI application to reach inside and execute the route handlers.

.. column::

    This test client provides all of the same methods and generally works as the `SanicTestClient`. The only difference is that you will need to add an `await` to each call:

.. column::

    ```python
    await app.test_client.get("/path/to/endpoint")
    ```

The `SanicASGITestClient` can be used in the exact same three ways as the `SanicTestClient`.


.. note::

    The `SanicASGITestClient` does not need to only be used with ASGI applications. The same way that the `SanicTestClient` does not need to only test sync endpoints. Both of these clients are capable of testing *any* Sanic application.


## Persistent service client: `ReusableClient`

This client works under a similar premise as the `SanicTestClient` in that it stands up an instance of your application and makes real HTTP requests to it. However, unlike the `SanicTestClient`, when using the `ReusableClient` you control the lifecycle of the application.

That means that every request **does not** start a new web server. Instead you will start the server and stop it as needed and can make multiple requests to the same running instance.

.. column::

    Unlike the other two clients, you **must** instantiate this client for use:

.. column::

    ```python
    from sanic_testing.reusable import ReusableClient

    client = ReusableClient(app)
    ```

.. column::

    Once created, you will use the client inside of a context manager. Once outside of the scope of the manager, the server will shutdown.

.. column::

    ```python
    from sanic_testing.reusable import ReusableClient

    def test_multiple_endpoints_on_same_server(app):
        client = ReusableClient(app)
        with client:
            _, response = client.get("/path/to/1")
            assert response.status == 200

            _, response = client.get("/path/to/2")
            assert response.status == 200
    ```
