Test Client

Make requests against your app without a running server

2 min read 417 words

Overview

TestClient sends requests through your app's ASGI handler directly -- no network, no running server. It uses the same Request and Responsetypes as production, so there is no translation layer to introduce bugs.

Note

Requires thetesting extra: pip install bengal-chirp[testing].

Basic Usage

from chirp.testing import TestClient

async def test_homepage():
    async with TestClient(app) as client:
        response = await client.get("/")
        assert response.status == 200
        assert "Hello" in response.text

The TestClientis an async context manager. It handles app startup/shutdown lifecycle automatically.

HTTP Methods

async def test_methods():
    async with TestClient(app) as client:
        # GET
        response = await client.get("/users")
        assert response.status == 200

        # POST with JSON
        response = await client.post("/users", json={"name": "Alice"})
        assert response.status == 201

        # POST with form data
        response = await client.post("/login", data={"username": "alice", "password": "secret"})

        # PUT
        response = await client.put("/users/1", json={"name": "Alice Updated"})

        # DELETE
        response = await client.delete("/users/1")
        assert response.status == 200

Custom Headers

async def test_with_headers():
    async with TestClient(app) as client:
        response = await client.get("/api/data", headers={
            "Authorization": "Bearer token123",
            "Accept": "application/json",
        })

Fragment Requests

Simulate htmx fragment requests:

async def test_fragment():
    async with TestClient(app) as client:
        # Send with HX-Request header to trigger fragment rendering
        response = await client.get("/search?q=test", headers={
            "HX-Request": "true",
            "HX-Target": "#results",
        })
        assert response.status == 200
        assert "<div id=\"results\">" in response.text
        # Fragment response -- no full page wrapper
        assert "<html>" not in response.text

The TestClientalso provides a convenience method:

async def test_fragment_convenience():
    async with TestClient(app) as client:
        response = await client.fragment("/search?q=test", target="#results")
        assert response.status == 200

Cookies

async def test_session():
    async with TestClient(app) as client:
        # Login sets a cookie
        await client.post("/login", data={"user": "alice", "pass": "secret"})

        # Subsequent requests include the cookie
        response = await client.get("/dashboard")
        assert response.status == 200
        assert "alice" in response.text

Response Properties

Property Type Description
status int HTTP status code
text str Response body as string
json dict Parsed JSON body
headers dict Response headers
cookies dict Response cookies

Using with pytest

import pytest
from myapp import app

@pytest.fixture
async def client():
    async with TestClient(app) as c:
        yield c

async def test_homepage(client):
    response = await client.get("/")
    assert response.status == 200

Next Steps