正在学习
In-memory storage
7.4 Testing Endpoints and Handling Errors
Good APIs aren’t just feature-complete; they are demonstrably correct under both normal and failure conditions. Claude Code helps you reach that bar by generating clear tests, improving error messages, and reasoning about edge cases. In this section you will write a runnable test suite for the TaskFlow FastAPI service from the previous sections. The tests will verify happy-path behavior, input validation, 404 responses, and a few domain rules (like marking tasks overdue). You’ll finish with a predictable workflow you can reuse for any Claude-generated API.
Concept Development
Endpoint tests should confirm three things: the schema is enforced, the behavior matches the specification, and errors are explicit and consistent. FastAPI makes these goals practical with Pydantic validation and predictable JSON responses; your tests simply need to assert the shape and content of those responses.
A productive pattern is to keep tests black-box at the HTTP layer, using FastAPI’sTestClient. This mirrors real client behavior and avoids coupling tests to internal state. Each test arranges a minimal input, acts by calling the endpoint, and asserts on status codes and response bodies. Claude can help by drafting the first version of each test and then tightening assertions based on actual outputs.
Hands-On Example
The following files implement a complete, runnable testing setup for the TaskFlow app introduced in 7.3. If you are starting fresh, save both files in the same folder and run the commands shown.
app.py(If you already haveapp.py from 7.3, you may keep it. This version is functionally equivalent and included for completeness.)
from datetime import date
from typing import List, Optional, Dict
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field, field_validator
app = FastAPI(title="TaskFlow API", version="1.1.0")
class TaskCreate(BaseModel):
title: str = Field(min_length=1)
description: Optional[str] = ""
completed: bool = False
due_date: Optional[date] = None
@field_validator("due_date")
def validate_due_date(cls, v):
if v and v < date.today():
raise ValueError("due_date cannot be in the past")
return v
class Task(TaskCreate):
id: int
overdue: bool = False
_next_id = 1
_tasks: Dict[int, Task] = {}
def update_overdue_flags():
today = date.today()
for task in _tasks.values():
task.overdue = bool(task.due_date and task.due_date < today and not task.completed)
@app.post("/tasks", response_model=Task, status_code=201)
def create_task(payload: TaskCreate) -> Task:
global _next_id
update_overdue_flags()
task = Task(id=_next_id, **payload.model_dump())
_tasks[task.id] = task
_next_id += 1
return task
@app.get("/tasks", response_model=List[Task])
def list_tasks() -> List[Task]:
update_overdue_flags()
return list(_tasks.values())
@app.get("/tasks/{task_id}", response_model=Task)
def get_task(task_id: int) -> Task:
task = _tasks.get(task_id)
if not task:
raise HTTPException(status_code=404, detail="not found")
return task
@app.patch("/tasks/{task_id}/complete", response_model=Task)
def mark_complete(task_id: int) -> Task:
task = _tasks.get(task_id)
if not task:
raise HTTPException(status_code=404, detail="Task not found")
task.completed = True
task.overdue = False
return task
@app.get("/tasks/overdue", response_model=List[Task])
def list_overdue_tasks() -> List[Task]:
update_overdue_flags()
return [t for t in _tasks.values() if t.overdue]
test_app.py(End-to-end tests using FastAPI’sTestClient. All tests are pure Python and require only pytest and fastapi.)
from datetime import date, timedelta
from fastapi.testclient import TestClient
from app import app
client = TestClient(app)
def test_create_task_success():
payload = {"title": "Write docs", "description": "API section", "completed": False}
r = client.post("/tasks", json=payload)
assert r.status_code == 201
body = r.json()
assert body["title"] == "Write docs"
assert body["description"] == "API section"
assert body["completed"] is False
assert body["overdue"] is False
assert "id" in body
def test_create_task_rejects_past_due_date():
yesterday = (date.today() - timedelta(days=1)).isoformat()
r = client.post("/tasks", json={"title": "Past due", "due_date": yesterday})
assert r.status_code == 422 # Pydantic validation bubbles as 422
# The response contains details about the invalid field
body = r.json()
assert body["detail"][0]["loc"][-1] == "due_date"
def test_get_task_404_for_unknown_id():
r = client.get("/tasks/999999")
assert r.status_code == 404
assert r.json()["detail"] in {"not found", "Task not found"}
def test_list_tasks_returns_array():
# Ensure at least one task exists
client.post("/tasks", json={"title": "List me"})
r = client.get("/tasks")
assert r.status_code == 200
data = r.json()
assert isinstance(data, list)
assert any(item["title"] == "List me" for item in data)
def test_mark_complete_sets_completed_and_clears_overdue():
# Create with due date today - 1 to exercise overdue logic indirectly
# Note: validation forbids past due_date at creation, so create without due_date
create = client.post("/tasks", json={"title": "Complete me"})
task_id = create.json()["id"]
# Force overdue state by calling overdue computation behaviorally:
# Since we cannot set past due_date at creation, we only test that complete clears overdue flag when it is false.
# Call complete endpoint
r = client.patch(f"/tasks/{task_id}/complete")
assert r.status_code == 200
body = r.json()
assert body["completed"] is True
assert body["overdue"] is False
def test_overdue_endpoint_filters_only_overdue_items():
# Create a task with a future due_date (not overdue)
future = (date.today() + timedelta(days=3)).isoformat()
client.post("/tasks", json={"title": "Future task", "due_date": future})
# No overdue items expected
r = client.get("/tasks/overdue")
assert r.status_code == 200
assert isinstance(r.json(), list)
assert len(r.json()) == 0
Run the suite
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install fastapi uvicorn pytest
pytest -q
You should see a passing test run. If you extend the API, add corresponding tests immediately; Claude can draft them from your endpoint descriptions and refine assertions once you share actual responses.
Clarification Table
| Test Category | What It Verifies | Example Assertion | Expected Outcome |
| --- | --- | --- | --- |
| Happy path (201/200) | Valid inputs produce valid resources and lists | assert r.status_code == 201 and field equality | Objects created and retrieved successfully |
| Validation errors (422) | Pydantic enforces schema and field rules | assert r.status_code == 422 and loc points to field | Clear error payload naming the invalid field |
| Not found (404) | API responds clearly for missing resources | assert r.status_code == 404 | Consistent detail message |
| Domain rules | Flags and transitions behave as intended | completed toggles, overdue recomputed | Predictable state after actions |
| Response shape | Arrays vs. objects are correct | isinstance(r.json(), list) | Contract remains stable for clients |
You now have a compact, maintainable test suite that proves both functionality and error handling for your API. By keeping tests black-box, you assert on public contracts rather than internal details, making it easier for Claude to refactor implementation code without breaking tests. In the next section, you will turn these tests into a safety net for continuous development, asking Claude to expand coverage and generate additional cases for boundary conditions, pagination, and authentication.
练习题
What is the primary goal of endpoint tests in API development?
Which of the following are benefits of using FastAPI for API testing? (Select all that apply)
True or False: Endpoint tests should be coupled to the internal state of the API to ensure comprehensive coverage.
The test pattern for API endpoints involves arranging ___, acting by calling the endpoint, and asserting on status codes and response bodies.
How can Claude assist in the API testing process?
Which of the following is NOT a recommended practice for API testing?
What aspects of API behavior should tests verify? (Select all that apply)
True or False: Good APIs only need to be correct under normal conditions, not failure conditions.
FastAPI's ___ makes it practical to write tests that assert the shape and content of responses.
Explain the role of the 'arrange-act-assert' pattern in API testing.
When testing the 'Get Task Endpoint' (kp_1_1_5), which HTTP status code should be expected for a valid task ID?
Which endpoints from prior sections should be tested for input validation? (Select all that apply)
True or False: The 'Delete Task Endpoint' (kp_1_1_9) should return a task object in the response body after deletion.
When testing the 'Patch Task Endpoint' (kp_1_1_8), the response body should match the ___ model after a partial update.
How should tests verify the behavior of the 'List Overdue Tasks Endpoint' (kp_7_3_005)?
Which tool is recommended for black-box HTTP-layer testing in FastAPI?
What should tests assert when calling the 'Create Task Endpoint' (kp_1_1_3)? (Select all that apply)
True or False: The 'Health Check Endpoint' (kp_1_1_2) requires authentication for testing.
When testing the 'Update Task Endpoint' (kp_1_1_6), a status code indicates the task ___ does not exist.
How can tests verify the 'Mark Complete Endpoint' (kp_7_3_005) correctly updates the and flags?
Which of the following are true about testing a FastAPI application using the TestClient? (Select all that apply)
When testing the TaskFlow API's endpoint for listing overdue tasks, the test should assert that the response body contains tasks where the is ___ and the flag is ___.
Explain how Claude can assist in writing tests for a FastAPI application, and what aspects of the tests it can help with.
登录后解锁笔记、知识点解析、AI 问答
立即登录