(二十一)核对请求、页面与失败后的记录
1. 用固定输入复现一次完整操作
# 项目文件:demos/api_roundtrip.py
# 登录后取得新令牌,提交JSON、读取列表、标记已读,所有动作操作同一数据库。
from webbook.lab import lab_client, login
with lab_client() as (client, _app, _database):
csrf = login(client)
headers = {"X-CSRF-Token": csrf}
response = client.post(
"/api/books",
json={"title": "Python Web", "finished": False, "tags": ["网络"]},
headers=headers,
)
book = response.json()
print("创建", response.status_code, response.headers["location"], book["finished"])
print("未读", len(client.get("/api/books?finished=0").json()["items"]))
response = client.post(f"/api/books/{book['id']}/finish", headers=headers)
print("标记", response.status_code, response.json()["finished"])
print("已读", len(client.get("/api/books?finished=1").json()["items"]))输出依次是创建 201、未读数量 1、标记后 True、已读数量 1。新增得到的 Location 可以继续 GET 查询;表单与 API 都能读到数据库里的同一份状态。
2. 数据库失败后回到事务起点
配套测试临时让 create_book 在执行插入后抛出数据库异常,再核对三个位置:客户端得到 500;服务器日志保留具体失败信息;新连接查询书籍数仍为 0。
这与“插入之前校验失败”不同。它实际经过了 INSERT,再通过事务回滚撤销,所以能验证失败时不会留下半次写入。
测试客户端按第六章 fixture 的方式取得应用与独立数据库:
# 项目文件:web_project/tests/conftest.py
# 每项请求检查独立建库,两个演示账号的摘要在这个测试库中生成。
import httpx
import pytest
from webbook.app import BookApp
from webbook.database import initialize
@pytest.fixture
def app(tmp_path):
path = tmp_path / "library.sqlite3"
initialize(path)
return BookApp(path, origin="http://book.test", cors_origins={"http://front.test"})
@pytest.fixture
def client(app):
with httpx.Client(
transport=httpx.WSGITransport(app=app),
base_url="http://book.test",
follow_redirects=False,
) as client:
yield client# 在web_project中检查字段、事务、会话、权限、转义与跨源响应。
uv run python -m pytest
3. 浏览器负责的行为单独观察
HTTPX 便于重复发送固定请求。表单导航、fetch、HTML 节点是否生成和 CORS 读取限制,还要实际经过浏览器。
配套核验使用浏览器完成登录、表单新增、刷新、按钮标记、JSON 显示、缺令牌写入、两种跨源来源和另一个账号的修改尝试。调试时也可以沿 Network 的一条请求,逐项对照输入、状态、Location、Cookie、响应文本与数据库记录。