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, Theout
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.
-
get
(path, data=None, headers=None, **kwargs)[source]¶ Send a GET request to the server. See request() for detais.
-
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 torequests.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 methodssend
,receive
andclose
, 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.