Testing MicroPie applications

This guide shows practical approaches for testing MicroPie applications. MicroPie does not ship with a bespoke test client, but because it is a regular ASGI application you can exercise it using familiar Python libraries such as unittest, pytest and httpx’s ASGI tools.

Choosing a test framework

MicroPie itself uses unittest.IsolatedAsyncioTestCase in tests.py to run asynchronous tests. pytest with the pytest-asyncio plugin offers a similar developer experience. Pick the library that best matches your project’s conventions—the examples below work with either.

Unit testing handlers directly

Because handlers are regular functions, you can instantiate your App subclass and call methods directly. Use the micropie.current_request context variable to set up any request state that your handler expects.

from micropie import App, Request, current_request

class MyApp(App):
    async def greet(self, name="World"):
        return f"Hello {name}!"

async def test_greet_uses_default():
    app = MyApp()
    scope = {"type": "http", "method": "GET", "path": "/"}
    request = Request(scope)
    token = current_request.set(request)
    try:
        response = await app.greet()
    finally:
        current_request.reset(token)
    assert response == "Hello World!"

Testing through the ASGI interface

For higher confidence, drive the full ASGI stack. httpx provides an ASGITransport class that can mount a MicroPie app. Install httpx with pip install httpx. The example below uses pytest style asserts, but the structure works in unittest with self.assertEqual.

import pytest
import httpx

from micropie import App

class MyApp(App):
    async def index(self):
        return {"status": "ok"}

@pytest.mark.asyncio
async def test_index_returns_json():
    app = MyApp()
    async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app)) as client:
        response = await client.get("http://test/")
    assert response.status_code == 200
    assert response.json() == {"status": "ok"}

Simulating sessions and middleware

To assert session behaviour, populate scope["headers"] with a cookie header and inspect the response headers for the updated Set-Cookie value. Middleware can be tested by attaching it to your app instance before issuing requests.

import httpx

from micropie import App, HttpMiddleware

class AddHeader(HttpMiddleware):
    async def before_request(self, request):
        return None

    async def after_request(self, request, status_code, response_body, extra_headers):
        extra_headers.append(("X-Test", "1"))
        return {"headers": extra_headers}

class MyApp(App):
    async def index(self):
        return "hi"

async def test_middleware_header():
    app = MyApp()
    app.middlewares.append(AddHeader())
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport) as client:
        response = await client.get("http://test/")
    assert response.headers["x-test"] == "1"

Handling lifespan events

If your application registers startup or shutdown handlers, wrap your ASGI client in a lifespan manager. httpx exposes httpx.ASGITransport with a lifespan="auto" mode that will run lifespan events before the first request and after the client exits (on versions that support this option).

async with httpx.AsyncClient(
    transport=httpx.ASGITransport(app=app, lifespan="auto")
) as client:
    ...

Further reading

  • Browse tests.py in the MicroPie source tree for additional patterns, including WebSocket testing helpers.

  • The httpx documentation has more on driving ASGI apps from tests.