最稳妥的数据库隔离方式是事务级回滚,pytest-django或sqlalchemy的test_transaction模式可自动在setup开启、teardown回滚事务,速度快且不写磁盘;但需注意其对commit后外部连接不可见,且无法拦截触发器或存储过程的硬写操作。

用 pytest 做集成测试时,数据库怎么清空才不串数据
测试之间必须隔离,否则前一个测试改了 users 表,后一个测试查不到预期记录,错误就藏在“看起来跑通了”的假象里。最稳妥的方式不是靠人工删表,而是让每个测试用独立数据库或事务回滚。
推荐优先用事务级隔离:pytest + pytest-django 或 sqlalchemy 的 test_transaction 模式能自动在 setUp 开事务、tearDown 回滚,不碰磁盘,快且干净。但注意:它对 COMMIT 后的外部连接(比如另一个进程查库)不可见,也拦不住触发器或存储过程里的硬写操作。
- PostgreSQL 用户慎用
TRUNCATE清表——它不走事务,会破坏回滚逻辑 - SQLite 内存数据库(
sqlite:///:memory:)适合单测,但集成测试若依赖 DB 特性(如外键行为、并发锁),内存库表现可能和真实环境不一致 - MySQL 8.0+ 支持
CREATE DATABASE test_db_123动态建库,配合随机后缀可彻底隔离,但启动慢、占资源,CI 里得配好连接池超时
alembic 升级脚本在测试环境怎么跳过或重定向
集成测试要跑在“已迁移完成”的库上,但你不能每次跑测试都真执行一遍 alembic upgrade head——它可能耗时、依赖网络、甚至因历史版本 bug 卡住。
正确做法是把测试数据库直接迁到目标版本,跳过中间步骤。用 alembic stamp head 把当前库标记为最新版(不执行 SQL),再让测试代码从干净状态重建 schema。前提是你的模型定义和 alembic 版本严格对齐,否则 stamp 后的表结构和 ORM 映射会错位。
立即学习“Python免费学习笔记(深入)”;
- 别在
conftest.py里无条件调alembic upgrade——CI 多并行任务会抢同一张alembic_version表,报duplicate key value violates unique constraint - 如果测试需要特定旧版本 schema(比如验证迁移逻辑本身),用
alembic downgrade -1回退一级,但必须确保 down revision 函数可逆且无副作用 -
alembic配置里设script_location = tests/alembic,和主项目分开,避免测试误用生产迁移脚本
测试数据库连接泄露导致 too many connections
Python 的 SQLAlchemy 或 asyncpg 连接不会自动关,尤其用了 sessionmaker 或 create_engine(pool_pre_ping=True) 却没显式 close(),跑几十个测试后连接数就爆了。
根本解法是绑定生命周期:用 pytest 的 yield_fixture 或 async def fixture 确保 engine.dispose() 或 pool.close() 必然执行。别信“退出进程自动清理”——pytest 可能复用进程,连接就卡着不动。
- 异步测试(
async def test_xxx)必须用await engine.dispose(),普通.dispose()会阻塞事件循环 - PostgreSQL 的
max_connections=100是全局限制,一个测试模块启 5 个 engine 实例 × 每个池大小 10 = 直接打满 - 加个检查:测试启动前执行
SELECT count(*) FROM pg_stat_activity WHERE datname = 'testdb';,超 5 就报警,早发现泄漏点
Docker Compose 启停数据库太慢,怎么提速
本地跑集成测试时,每轮都 docker-compose up -d db + 等健康检查,光等 PostgreSQL ready 就要 5–8 秒,拖慢反馈节奏。
实际不需要每次重启容器。用 docker-compose exec db psql -U testuser -c "DROP DATABASE testdb; CREATE DATABASE testdb;" 清库比重建容器快 3 倍以上。前提是容器一直运行,且数据库用户有 CREATEDB 权限。
- 别用
depends_on: { condition: service_healthy }——Docker 默认健康检查只 ping 端口,PostgreSQL 可能端口通了但还没接受连接,得自己写脚本查pg_isready - 测试镜像里预装
pg_isready和mysqladmin,比用curl http://db:5432可靠得多 - CI 中用
cache_from缓存带数据库的测试镜像,避免每次拉取 500MB 的 PostgreSQL 官方镜像
数据库管理真正的复杂点不在“怎么清”,而在“什么时候清”——事务回滚快但掩盖了真实提交路径;动态建库彻底但拖慢 CI;共享容器省事但要求所有测试遵守同一套连接生命周期。选哪条路,取决于你愿为隔离性多付多少调试时间。










