You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI异步Pytest测试报错:数据库占用及AsyncClient复用问题

解决FastAPI异步Pytest测试的三个报错问题

1. 解决数据库被占用的OperationalError

问题原因

测试结束后SQLAlchemy连接池仍有活跃连接未释放,导致删除测试数据库时提示被其他用户占用。

解决方案

在删除测试数据库前,强制终止所有连接到该库的外部会话,同时用with语句管理连接确保自动释放资源。修改测试数据库操作代码:

if is_testing: 
    db_url = self._engine.url
    if db_url.host != "localhost":
        raise Exception("db host must be 'localhost' in test environment")
    except_schema_db_url = f"{db_url.drivername}://{db_url.username}@{db_url.host}"   
    schema_name = db_url.database         # test                                                    
    temp_engine = create_engine(except_schema_db_url, echo=echo, pool_recycle=pool_recycle, pool_pre_ping=True)
    
    with temp_engine.connect() as conn:
        conn = conn.execution_options(autocommit=True)  # 启用自动提交避免事务阻塞
        
        # 强制终止所有连接到测试库的外部会话(排除当前连接)
        conn.execute(f"""
            SELECT pg_terminate_backend(pid) 
            FROM pg_stat_activity 
            WHERE datname = '{schema_name}' 
              AND pid <> pg_backend_pid();
        """)
        
        try:
            conn.execute(f"DROP DATABASE {schema_name}")
            print(f"test db dropped! about to create {schema_name}")
        except ProgrammingError:
            print(f"could not drop the database, probably does not exist.")
        except OperationalError:
            print("could not drop database because it's being accessed by other users(psql prompt open?)")
        
        conn.execute(f"CREATE DATABASE {schema_name}")
        try:
            conn.execute(f"create user test with encrypted password 'test'")
        except:
            print("User already exist")
    
    temp_engine.dispose()

2. 解决AsyncClient不可调用的TypeError

问题原因

测试代码错误地将AsyncClient实例当作函数调用(如client()),而非使用其HTTP方法(如get()、post())。

解决方案

检查所有测试用例,确保调用AsyncClient的正确方法:

错误示例:

response = client()  # 错误:AsyncClient实例不可调用

正确示例:

response = await client.get("/api/endpoint")  # 正确:使用对应HTTP方法

3. 解决ClientState重复打开/关闭的错误

问题原因

会话级(scope="session")的AsyncClient被多个测试复用,部分测试可能关闭客户端导致后续测试无法重新打开;且原fixture未使用异步装饰器适配AsyncClient特性。

解决方案

将client fixture作用域改为function,确保每个测试使用独立实例;同时用pytest_asyncio.fixture处理异步逻辑:

修改conftest.py的client fixture:

import pytest_asyncio
from httpx import AsyncClient

@pytest_asyncio.fixture(scope="function")
async def client(app):
    # 用async with管理客户端生命周期,测试后自动关闭
    async with AsyncClient(app=app, base_url="http://test") as client:
        yield client

若使用异步SQLAlchemy,同步修改session fixture为异步:

@pytest_asyncio.fixture(scope="function", autouse=True)
async def session():
    async with db.session() as sess:
        yield sess
        await clear_all_table_data(session=sess, metadata=Base.metadata, except_tables=[])
        await sess.rollback()

async def clear_all_table_data(session: AsyncSession, metadata, except_tables: List[str] = None):
    await session.execute("SET session_replication_role = 'replica';")          
    for table in metadata.sorted_tables:
        if table.name not in except_tables:
            await session.execute(table.delete())
    await session.execute("SET session_replication_role = 'origin';")         
    await session.commit()

内容的提问来源于stack exchange,提问作者giwony

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 20:45:14