Testing

Responder includes a built-in test client powered by Starlette’s TestClient. You don’t need to start a server — tests run in-process, making them fast and reliable.

Getting Started

Given a simple application in api.py:

import responder

api = responder.API()

@api.route("/")
def hello(req, resp):
    resp.text = "hello, world!"

if __name__ == "__main__":
    api.run()

You can test it with pytest:

# test_api.py
import api as service

def test_hello():
    r = service.api.requests.get("/")
    assert r.text == "hello, world!"

Run your tests:

$ pytest

Using Fixtures

For larger test suites, use pytest fixtures to share the API instance across tests:

import pytest
import api as service

@pytest.fixture
def api():
    return service.api

def test_hello(api):
    r = api.requests.get("/")
    assert r.text == "hello, world!"

def test_json(api):
    @api.route("/data")
    def data(req, resp):
        resp.media = {"key": "value"}

    r = api.requests.get(api.url_for(data))
    assert r.json() == {"key": "value"}

The api.url_for() method generates a URL for a given route endpoint, so you don’t have to hard-code paths in your tests.

Testing JSON APIs

Send JSON data and check the response:

def test_create_item(api):
    @api.route("/items")
    async def create(req, resp):
        data = await req.media()
        resp.media = {"created": data}
        resp.status_code = 201

    r = api.requests.post(api.url_for(create), json={"name": "widget"})
    assert r.status_code == 201
    assert r.json() == {"created": {"name": "widget"}}

Testing File Uploads

Send files using the files parameter:

def test_upload(api):
    @api.route("/upload")
    async def upload(req, resp):
        files = await req.media("files")
        resp.media = {"received": list(files.keys())}

    files = {"doc": ("report.pdf", b"content", "application/pdf")}
    r = api.requests.post(api.url_for(upload), files=files)
    assert r.json() == {"received": ["doc"]}

Testing WebSockets

Use Starlette’s TestClient directly for WebSocket connections:

from starlette.testclient import TestClient

def test_websocket(api):
    @api.route("/ws", websocket=True)
    async def ws(ws):
        await ws.accept()
        await ws.send_text("hello")
        await ws.close()

    client = TestClient(api)
    with client.websocket_connect("/ws") as ws:
        assert ws.receive_text() == "hello"

Testing Error Handling

To test error responses without pytest raising the exception, disable server exception propagation:

from starlette.testclient import TestClient

def test_500(api):
    @api.route("/fail")
    def fail(req, resp):
        raise ValueError("something broke")

    client = TestClient(api, raise_server_exceptions=False)
    r = client.get(api.url_for(fail))
    assert r.status_code == 500

Testing Lifespan Events

The test client supports lifespan events. Use with to ensure startup and shutdown hooks run:

def test_with_lifespan(api):
    started = {"value": False}

    @api.on_event("startup")
    async def on_startup():
        started["value"] = True

    @api.route("/")
    def check(req, resp):
        resp.media = {"started": started["value"]}

    with api.requests as session:
        r = session.get("http://;/")
        assert r.json() == {"started": True}