Python 集成测试的数据库管理方案

4次阅读

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

Python 集成测试的数据库管理方案

pytest 做集成测试时,数据库怎么清空才不串数据

测试之间必须隔离,否则前一个测试改了 users 表,后一个测试查不到预期记录,错误就藏在“看起来跑通了”的假象里。最稳妥的方式不是靠人工删表,而是让每个测试用独立数据库或事务回滚。

推荐优先用事务级隔离:pytest + pytest-djangosqlalchemytest_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

pythonSQLAlchemyasyncpg 连接不会自动关,尤其用了 sessionmakercreate_engine(pool_pre_ping=True) 却没显式 close(),跑几十个测试后连接数就爆了。

根本解法是绑定生命周期:用 pytestyield_fixtureasync 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_isreadymysqladmin,比用 curl http://db:5432 可靠得多
  • CI 中用 cache_from 缓存带数据库的测试镜像,避免每次拉取 500MB 的 PostgreSQL 官方镜像

数据库管理真正的复杂点不在“怎么清”,而在“什么时候清”——事务回滚快但掩盖了真实提交路径;动态建库彻底但拖慢 CI;共享容器省事但要求所有测试遵守同一套连接生命周期。选哪条路,取决于你愿为隔离性多付多少调试时间。

text=ZqhQzanResources