创建时间: 2026-09-01最后更新: 2026-09-01作者: yangbo(2d5d2525a)
1. 测试库
数据库测试应该连接独立测试数据库,绝不能把开发库或生产库拿来清空和回滚。
在 compose.yaml 中增加测试服务:
compose.yaml
01services:02postgres:03image: postgres:1804environment:05POSTGRES_DB: task_app06POSTGRES_USER: task_app07POSTGRES_PASSWORD: task_app_dev_password08ports:09- "5432:5432"10volumes:11- postgres_data:/var/lib/postgresql1213postgres_test:14image: postgres:1815environment:16POSTGRES_DB: task_app_test17POSTGRES_USER: task_app_test18POSTGRES_PASSWORD: task_app_test_password19ports:20- "5433:5432"21profiles: ["test"]2223volumes:24postgres_data:
启动测试数据库:
start-test-database.bash
1docker compose --profile test up -d postgres_test
测试环境使用不同端口、数据库名和账号:
.env.test
1DATABASE_URL=postgresql+psycopg://task_app_test:task_app_test_password@localhost:5433/task_app_test
测试启动前先执行全部迁移:
migrate-test-database.bash
1env -u DATABASE_URL uv run --env-file .env.test alembic upgrade head
uv 会优先使用当前 Shell 已经存在的环境变量,因此命令先移除可能残留的 DATABASE_URL,再从 .env.test 加载测试连接。这样测试验证的是部署会使用的真实迁移结构,而不是另外维护一套 create_all() 测试结构。
测试工具
安装测试依赖:
install-test-tools.bash
1uv add --group test pytest pytest-asyncio httpx asgi-lifespan
在 pyproject.toml 中配置异步测试模式:
pyproject.toml
1[tool.pytest.ini_options]2asyncio_mode = "auto"3asyncio_default_fixture_loop_scope = "function"4asyncio_default_test_loop_scope = "function"5testpaths = ["tests"]
asyncio_mode = "auto" 让 pytest 自动识别异步测试和异步 Fixture。这里又把测试与 Fixture 的事件循环范围明确设为 function,每个用例都会获得独立事件循环,测试之间不会意外共享异步任务。
创建测试 Engine 时必须再次确认 URL 指向测试库:
tests/database.py
01from sqlalchemy.engine import make_url02from sqlalchemy.ext.asyncio import create_async_engine03from sqlalchemy.pool import NullPool0405from app.core.settings import settings060708url = make_url(settings.database_url)09if not (url.database or "").endswith("_test"):10raise RuntimeError("数据库测试只能连接名称以 _test 结尾的数据库")1112test_engine = create_async_engine(13settings.database_url,14poolclass=NullPool,15)
测试 Engine 定义在模块级,会被不同用例的事件循环使用。NullPool 不复用驱动连接,可以避免某个用例创建的连接被下一个事件循环再次取出。另一种做法是让所有相关测试共享同一个事件循环,并在退出时释放 Engine;两种方式选择一种即可。
名称检查不是绝对安全证明,但可以阻止最常见的环境变量误配。CI 还应该使用专门的临时数据库账号和网络环境。运行测试时同样要显式加载测试环境变量:
run-database-tests.bash
1env -u DATABASE_URL uv run --env-file .env.test pytest
环境变量必须在 Python 进程启动前加载。否则 settings 可能已经从默认 .env 创建并缓存,等测试代码开始执行时再修改环境变量已经太迟。
正在验证登录状态
请稍候,验证完成后将继续显示文章内容