1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
|
.. _testing:
Testing
=======
Quart's usage of global variables (``request`` etc) makes testing any
code that uses these variables more difficult. To combat this it is
best practice to only use these variables in the code directly called
by Quart e.g. route functions or before request functions. Thereafter
Quart provides a testing framework to control these globals.
Primarily testing should be done using a test client bound to the
Quart app being tested. As this is so common there is a helper method
:meth:`~quart.app.Quart.test_client` which returns a bound client,
e.g.
.. code-block:: python
async def test_app(app):
client = app.test_client()
response = await client.get('/')
assert response.status_code == 200
Event loops
-----------
To test with quart you will need to have an event loop in order to
call the async functions. This is possible to do manually, for example
.. code-block:: python
def aiotest(func):
loop = asyncio.get_event_loop()
loop.run_until_complete(func())
@aiotest
async def test_app(app)
...
However it is much easier to use ``pytest-asyncio`` and the to do this
for you. Note that ``pytest`` is the recommended test runner and the
examples throughout assume ``pytest`` is used with ``pytest-asyncio``.
Calling routes
--------------
The test client has helper methods for all the HTTP verbs
e.g. :meth:`~quart.testing.QuartClient.post`. These are helper methods
for :meth:`~quart.testing.QuartClient.open`, as such all the methods at
a minimum expect a path and optionally can have query parameters, json
or form data. A standard :class:`~quart.wrappers.Response` class is
returned. An example:
.. code-block:: python
async def test_create(app):
test_client = app.test_client()
data = {'name': 'foo'}
response = await test_client.post('/resource/', json=data)
assert response.status_code == 201
result = await response.get_json()
assert result == data
To test test routes which stream requests or responses, use the
:meth:`~quart.testing.client.QuartClient.request` method:
.. code-block:: python
async def test_stream() -> None:
test_client = app.test_client()
async with test_client.request(...) as connection:
await connection.send(b"data")
await connection.send_complete()
...
# receive a chunk of the response
data = await connection.receive()
...
# assemble the rest of the response without the first bit
response = await connection.as_response()
To learn more about streaming requests and responses, read :ref:`request_body`
and :ref:`streaming_response`.
Context testing
---------------
It is often necessary to test something within the app or request
contexts. This is simple enough for the app context,
.. code-block:: python
async def test_app_context(app):
async with app.app_context():
current_app.[use]
for the request context however the request context has to be faked,
at a minimum this means the method and path must be supplied, e.g.
.. code-block:: python
async def test_app_context(app):
async with app.test_request_context("/", method="GET"):
request.[use]
.. note::
Any ``before_request`` or ``after_request`` functions are not
called when using the ``test_request_context``. You can add
``await app.preprocess_request()`` to ensure the
``before_request`` functions are called.
.. code-block:: python
async def test_app_context(app):
async with app.test_request_context("/", method="GET"):
await app.preprocess_request()
# The before_request functions have now been called
request.[use]
|