根本解法是绑定连接生命周期:每个fixture必须在try/finally中显式调用dispose()或close(),而非依赖gc或session.close();async fixture需await pool.close(),并注意pool_pre_ping、随机库名及xdist作用域问题。

根本解法不是“修漏”,而是绑定连接生命周期:每个 fixture 必须显式 dispose 或 close,且必须在 yield 之后、或 finally 块中执行。
为什么 connection 泄露比你想象的更隐蔽
SQLAlchemy 的 create_engine 默认启用连接池,engine.connect() 返回的是池中连接的代理对象,不调用 close() 或 dispose(),该连接不会真正归还池中——它只是被标记为“可用”,但底层 socket 和认证会话仍驻留。pytest 并行跑 50 个测试时,pool_size=5 却看到数据库活跃连接数飙到 60+,就是这个原因。
常见错误现象包括:
- CI 报错
too many connections,但本地单测不复现(并行放大问题) - 测试中途
ps aux | grep python查到大量idle in transaction连接 - PostgreSQL 日志里反复出现
could not fork new process
fixture 中必须用 try/finally + dispose() 显式清理
别依赖 __del__ 或 GC:SQLAlchemy 不保证连接对象能被及时回收;也别只靠 session.close():它只关 session,不碰 engine 底层连接池。
正确写法示例(以 PostgreSQL 为例):
@pytest.fixture(scope="function")
def db_engine():
engine = create_engine("postgresql://user:pass@localhost/testdb")
try:
yield engine
finally:
engine.dispose() # 关键:强制清空连接池所有连接
如果你用的是 sessionmaker,也要在 fixture 中绑定生命周期:
@pytest.fixture(scope="function")
def db_session(db_engine):
Session = sessionmaker(bind=db_engine)
session = Session()
try:
yield session
finally:
session.close() # 关 session
db_engine.dispose() # 关底层连接池
异步测试(如 asyncpg / aiomysql)泄漏更难察觉
asyncpg 的 create_pool() 返回的 pool 对象,必须显式调用 pool.close() 并 await,否则协程结束、event loop 关闭后连接仍挂在后台。
典型错误写法:
-
pool = await create_pool(...)后没await pool.close() - 在
async def fixture中 yield pool,但没用async with或await pool.close()收尾 - 用
pytest-asyncio但 fixture scope 混用 function/module,导致 pool 被复用多次却只 close 一次
安全做法:
@pytest.fixture(scope="function")
async def pg_pool():
pool = await asyncpg.create_pool(
"postgresql://user:pass@localhost/testdb",
min_size=1,
max_size=3
)
try:
yield pool
finally:
await pool.close() # 必须 await
容易被忽略的三个点
一是 pool_pre_ping=True 不能代替 dispose:它只在每次取连接前发一个 SELECT 1 探活,不解决连接堆积;二是 CI 多 job 并行时,不同 job 共享同一数据库实例,必须用随机库名(如 f"test_{int(time.time())}_{random.randint(1000,9999)}"),否则 dispose 会误清别人正在用的连接;三是 pytest-xdist 的 --dist=loadscope 模式下,module 级 fixture 可能跨测试复用 engine,此时 scope="function" 才是唯一稳妥选择。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











