Skip to content

Testing

JustAPI includes JustAPITestClient for testing without running a server. It processes requests in-process using the Rust runtime.

from justapi import JustAPIApp, JustAPITestClient
app = JustAPIApp()
@app.get("/hello")
def hello():
return {"message": "Hello!"}
client = JustAPITestClient(app)
def test_hello():
response = client.get("/hello")
assert response.status_code == 200
assert response.json() == {"message": "Hello!"}
def test_create_item():
response = client.post("/items", body={"name": "Widget", "price": 9.99})
assert response.status_code == 200
assert response.json()["name"] == "Widget"
@app.get("/protected")
def protected(x_token: str = Header(...)):
return {"token": x_token}
def test_with_header():
response = client.get("/protected", headers={"X-Token": "secret"})
assert response.status_code == 200
assert response.json() == {"token": "secret"}
def test_not_found():
response = client.get("/nonexistent")
assert response.status_code == 404
def test_validation_error():
response = client.post("/items", body={"name": "x"})
assert response.status_code == 422
assert "detail" in response.json()
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {"user_id": user_id}
def test_get_user():
response = client.get("/users/42")
assert response.json() == {"user_id": 42}
@app.get("/search")
def search(q: str, limit: int = 10):
return {"q": q, "limit": limit}
def test_search_with_defaults():
response = client.get("/search?q=foo")
assert response.json() == {"q": "foo", "limit": 10}
def test_search_with_custom_limit():
response = client.get("/search?q=foo&limit=5")
assert response.json() == {"q": "foo", "limit": 5}

Here’s a complete example with a mini CRUD API and tests:

from justapi import JustAPIApp, JustAPITestClient, HTTPException
app = JustAPIApp()
items_db = {}
@app.post("/items", status_code=201)
def create_item(item: dict):
item_id = len(items_db) + 1
items_db[item_id] = item
return {"id": item_id, **item}
@app.get("/items/{item_id}")
def get_item(item_id: int):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Not found")
return {"id": item_id, **items_db[item_id]}
@app.get("/items")
def list_items():
return [{"id": k, **v} for k, v in items_db.items()]
@app.delete("/items/{item_id}", status_code=204)
def delete_item(item_id: int):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Not found")
del items_db[item_id]

Tests:

client = JustAPITestClient(app)
def test_create_and_get():
# Create
response = client.post("/items", body={"name": "Widget", "price": 9.99})
assert response.status_code == 201
item_id = response.json()["id"]
# Get
response = client.get(f"/items/{item_id}")
assert response.status_code == 200
assert response.json()["name"] == "Widget"
def test_list_items():
response = client.get("/items")
assert response.status_code == 200
assert isinstance(response.json(), list)
def test_get_nonexistent():
response = client.get("/items/99999")
assert response.status_code == 404
def test_delete_item():
# Create first
response = client.post("/items", body={"name": "To Delete"})
item_id = response.json()["id"]
# Delete
response = client.delete(f"/items/{item_id}")
assert response.status_code == 204
# Confirm deleted
response = client.get(f"/items/{item_id}")
assert response.status_code == 404
Terminal window
# Run with pytest
pytest tests/ -v
# Run with uv
uv run pytest tests/ -v
# Single test file
pytest tests/test_app.py -v
# Run with coverage
pytest --cov=myapp tests/
tests/
├── test_items.py
├── test_users.py
├── test_auth.py
└── conftest.py