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

FastAPI异步MySQL环境下实现Pytest用例事务隔离自动回滚

问题:FastAPI异步应用测试时自动回滚数据库变更

我有一个基于FastAPI、MySQL和asyncio的异步应用,希望为其集成测试用例,实现每个测试用例执行后自动回滚数据库变更,保证用例间的隔离性。

现有控制器配置(注入数据库连接依赖)

from sqlalchemy.ext.asyncio import create_async_engine

async def get_db_connection_dependency():
    engine = create_async_engine("mysql+aiomysql://root:root@mysql8:3306/user_db")
    connection = engine.connect()
    return connection

class UserController:
   async def create_user(
            self,
            request: Request,
            connection: AsyncConnection = Depends(get_db_connection_dependency)

    ) -> JSONResponse:
        
        # START TRANSACTION
        await connection.__aenter__()
        transaction = connection.begin()
        await transaction.__aenter__()

        try:
            do_stuff()
        except:
            await transaction.rollback()
        else:
            await transaction.commit()
        finally:
            await connection.close()
      
        # END TRANSACTION
        
        return JSONResponse(status_code=201)

现有Pytest测试用例

import pytest

app = FastAPI()

@pytest.fixture()
def client():
    with TestClient(app=app) as c:
        yield c

class TestUserCreation:
    CREATE_USER_URL = "/users/create"
    
    def test_create_user(self, client):
        response = client.post(self.CREATE_USER_URL, json={"name": "John"})
        assert response.status_code == 201

当前测试用例可正常运行,但会将新创建的用户持久化到数据库中,我需要实现测试结束后自动回滚变更。

我尝试过的方案都不适用:

  • 使用factory objects:但我的控制器直接依赖数据库连接并操作数据库,无法使用该方案;
  • 手动注入依赖:想在测试前创建连接注入,测试后回滚,但没找到可行的注入方式;
  • FastAPI官方文档对数据库测试回滚的介绍不够详尽。

我想到过通过中间件附加连接到请求对象,根据环境变量在测试环境强制回滚,但感觉过度设计。

请问FastAPI是否有内置或现成的库实现该需求?若Pytest不适合,也可更换测试框架。


解决方案

核心思路是在测试时替换数据库连接依赖,为每个测试用例创建一个带事务的数据库连接,测试结束后自动回滚事务,无需修改业务代码,也不需要过度设计中间件。以下是具体实现步骤:

1. 重构数据库依赖,提升可测试性

首先把引擎创建逻辑抽离,避免重复创建引擎,同时简化依赖的上下文管理:

# database.py
from sqlalchemy.ext.asyncio import create_async_engine, AsyncConnection

# 全局单例引擎(生产/测试环境复用)
engine = create_async_engine("mysql+aiomysql://root:root@mysql8:3306/user_db")

async def get_db_connection_dependency():
    async with engine.connect() as connection:
        yield connection

同时简化控制器的事务逻辑,利用SQLAlchemy上下文管理器自动处理事务生命周期:

class UserController:
   async def create_user(
            self,
            request: Request,
            connection: AsyncConnection = Depends(get_db_connection_dependency)

    ) -> JSONResponse:
        try:
            async with connection.begin() as transaction:
                # 执行实际数据库操作,例如插入用户
                await connection.execute(insert_stmt, {"name": (await request.json())["name"]})
                # 无异常时自动提交事务
        except Exception as e:
            # 异常时自动回滚
            await transaction.rollback()
            raise e
        
        return JSONResponse(status_code=201)

2. 编写测试用例的数据库Fixture

使用pytest-asyncio创建异步Fixture,为每个测试用例生成独立的事务连接,并替换原有的依赖:

import pytest
from sqlalchemy.ext.asyncio import AsyncConnection
from fastapi.testclient import AsyncTestClient
from your_app import app, engine, get_db_connection_dependency

# 异步测试客户端Fixture
@pytest.fixture(scope="function")
async def async_client():
    async with AsyncTestClient(app=app) as client:
        yield client

# 带事务的数据库连接Fixture,每个测试用例独立
@pytest.fixture(scope="function")
async def test_db_connection():
    async with engine.connect() as connection:
        # 开启事务
        await connection.begin()
        yield connection
        # 测试结束后强制回滚,不提交到数据库
        await connection.rollback()

# 自动替换FastAPI的数据库依赖
@pytest.fixture(autouse=True)
async def override_db_dependency(test_db_connection: AsyncConnection):
    async def override_get_db():
        yield test_db_connection
    app.dependency_overrides[get_db_connection_dependency] = override_get_db
    yield
    # 测试完成后清除依赖覆盖
    app.dependency_overrides.clear()

3. 编写异步测试用例

适配异步接口,使用async def测试函数:

class TestUserCreation:
    CREATE_USER_URL = "/users/create"
    
    async def test_create_user(self, async_client):
        response = await async_client.post(self.CREATE_USER_URL, json={"name": "John"})
        assert response.status_code == 201
        # 可额外查询数据库验证操作生效(测试结束后会被回滚)

关键说明

  • 依赖覆盖:FastAPI的app.dependency_overrides是官方推荐的测试依赖替换方式,无需修改业务代码;
  • 事务隔离:每个测试用例使用独立事务,测试结束后回滚,完全保证用例间的数据隔离;
  • 异步适配:使用AsyncTestClient替代同步TestClient,适配异步接口测试;
  • 无额外依赖:仅使用FastAPI、SQLAlchemy和pytest-asyncio,无需第三方工具库。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 23:30:30