Asgineer test utilities

When you’ve writting a fancy web application with Asgineer, you might want to write some unit tests. The asgineer.testutils module provides some utilities to help do that. It requires the requests library, and the websockets library when using websockets.

Testing example

import json

from asgineer.testutils import ProcessTestServer, MockTestServer


# ----- Define handlers - you'd probably import these instead

async def main_handler(request):
    if request.path.startswith('/api/'):
        return await api_handler(request)
    else:
        return "Hello world!"


async def api_handler(request):
    return {'welcome': "This is a really silly API"}


# ----- Test functions

def test_my_app():

    with MockTestServer(api_handler) as p:
        r = p.get('/api/')

    assert r.status == 200
    assert "welcome" in json.loads(r.body.decode())


    with MockTestServer(main_handler) as p:
        r = p.get('')

    assert r.status == 200
    assert "Hello" in r.body.decode()


if __name__ == '__main__':
    # Important: don't call the test functions from the root,
    # since this module gets re-imported!

    test_my_app()

Instead of the MockTestServer you can also use the ProcessTestServer to test with a real server like uvicorn running in a subprocess. The API is exactly the same though!

Test server classes

class asgineer.testutils.BaseTestServer(app, server_description, *, loop=None)[source]

Base class for test servers. Objects of this class represent an ASGI server instance that can be used to test your server implementation.

The app object passed to the constructor can be an ASGI application or an async (Asgineer-style) handler.

The server can be started/stopped by using it as a context manager. The url attribute represents the url that can be used to make requests to the server. When the server has stopped, The out attribute contains the server output (stdout and stderr).

Only one instance of this class (per process) should be used (as a context manager) at any given time.

app

The application object that was given at instantiation.

delete(path, data=None, headers=None, **kwargs)[source]

Send a DELETE request to the server. See request() for detais.

filter_lines(lines)[source]

Overloadable line filter.

get(path, data=None, headers=None, **kwargs)[source]

Send a GET request to the server. See request() for detais.

log(*messages, sep=' ', end='\n')[source]

Log a message. Overloadable. Default write to stdout.

out

The stdout / stderr of the server. This gets set when the with-statement using this object exits.

post(path, data=None, headers=None, **kwargs)[source]

Send a POST request to the server. See request() for detais.

put(path, data=None, headers=None, **kwargs)[source]

Send a PUT request to the server. See request() for detais.

request(method, path, data=None, headers=None, **kwargs)[source]

Send a request to the server. Returns a named tuple (status, headers, body).

Arguments:
method (str): the HTTP method (e.g. “GET”) path (str): path or url (also see the url property). data: the bytes to send (optional). headers: headers to send (optional). kwargs: additional arguments to pass to requests.request().
url

The url at which the server is listening.

ws_communicate(path, client_co_func, loop=None)[source]

Do a websocket request and communicate over the connection.

The client_co_func object must be an async function, it receives a ws object as an argument, which has methods send, receive and close, and it can be iterated over. Messages are either str or bytes.

class asgineer.testutils.ProcessTestServer(app, server, **kwargs)[source]

Subclass of BaseTestServer that runs an actual server in a subprocess. The server argument must be a server supported by Asgineer’ run() function, like “uvicorn”, “hypercorn” or “daphne”.

This provides a very realistic approach to test server applicationes, though the overhead of starting and stopping the server costs about a second, and its hard to measure code coverage in this way. Therefore this approach is most suited for higher level / integration tests.

Requests can be done via the methods of this object, or using any other request library.

class asgineer.testutils.MockTestServer(app, **kwargs)[source]

Subclass of BaseTestServer that mocks an ASGI server and operates in-process. This is a less realistic approach, but faster and allows tracking test coverage, so it’s more suited for unit tests.

Requests must be done via the methods of this object. The used url can be anything.