如何解决FastAPI生命周期事件自动化测试失败问题
问题:FastAPI Lifespan 函数调用的自动化测试失败
我有一个FastAPI项目,定义了如下生命周期事件:
@asynccontextmanager async def lifespan(app: FastAPI): # Starting up IAM APP if mm_settings.MIGRATE_CONFIGURATION: await migrate_configuration_service_providers() yield app = FastAPI(root_path=mm_settings.ROOT_PATH, lifespan=lifespan)
我希望编写自动化测试,确保migrate_configuration_service_providers()及其内部函数被调用,但所有测试均失败。
尝试使用httpx.AsyncClient和FastAPI TestClient编写测试基类:
class LifespanTestBase(AsyncTestBase): @pytest.fixture() def test_client(self): with TestClient(app=app, base_url="http://test") as client: yield client @pytest_asyncio.fixture() async def async_client(self): async with AsyncClient(app=app, base_url="http://test") as client: yield client
对应的测试代码如下:
class TestLifespan(LifespanTestBase): @patch("iam.util.migrations.migrate_configuration_service_providers") # @pytest.mark.asyncio def test_should_call_migrate_configuration_service_providers( self, migrate_configuration_service_providers_mock: Mock, test_client: TestClient, ): test_client.get("/") migrate_configuration_service_providers_mock.assert_called() @patch("iam.util.migrations.migrate_configuration_service_providers") @pytest.mark.asyncio async def test_should_call_migrate_configuration_service_providers2( self, migrate_configuration_service_providers_mock: Mock, async_client: AsyncClient, ): await async_client.get("/") migrate_configuration_service_providers_mock.assert_called()
两类测试均失败,但手动运行应用时migrate_configuration_service_providers()可正常执行(能看到数据库变更)。怀疑测试失败可能是因为app未挂载路由,调用/接口返回404导致,请问能否编写有效的自动化测试,该如何解决此问题?
解决方案
1. 确认测试环境的配置开关
lifespan中仅当mm_settings.MIGRATE_CONFIGURATION为True时才会执行迁移函数,测试时必须确保这个配置处于开启状态:
def test_lifespan_migration(): # 强制开启配置 mm_settings.MIGRATE_CONFIGURATION = True # 后续测试逻辑
2. 直接测试lifespan逻辑,无需依赖接口
不需要调用任何接口,直接手动运行lifespan上下文即可触发启动阶段的代码,这样能排除路由问题的干扰:
@pytest.mark.asyncio async def test_lifespan_triggers_migration(): mm_settings.MIGRATE_CONFIGURATION = True # 替换为lifespan所在模块中引用的函数路径 with patch("app.main.migrate_configuration_service_providers") as mock: async with lifespan(app): # 进入上下文时会执行yield前的迁移逻辑 mock.assert_called_once()
3. 修正Mock的路径(核心问题)
Python的Mock是作用于模块的命名空间,必须确保你Mock的是被调用位置所引用的函数对象。比如:
- 如果你的
lifespan函数在app/main.py中,并且是通过from iam.util.migrations import migrate_configuration_service_providers导入的,那么Mock的路径应该是app.main.migrate_configuration_service_providers,而非iam.util.migrations.migrate_configuration_service_providers。
错误的Mock路径会导致Mock对象从未被调用,这是测试失败的常见原因。
4. 利用客户端上下文触发lifespan
如果坚持使用客户端测试,注意TestClient和AsyncClient在进入上下文时会自动执行lifespan的启动逻辑,不需要调用接口:
- 同步客户端测试:
def test_sync_client_triggers_lifespan(): mm_settings.MIGRATE_CONFIGURATION = True with patch("app.main.migrate_configuration_service_providers") as mock: with TestClient(app=app): # 进入上下文即触发lifespan启动 mock.assert_called_once()
- 异步客户端测试:
@pytest.mark.asyncio async def test_async_client_triggers_lifespan(): mm_settings.MIGRATE_CONFIGURATION = True with patch("app.main.migrate_configuration_service_providers") as mock: async with AsyncClient(app=app, base_url="http://test"): mock.assert_called_once()
内容的提问来源于stack exchange,提问作者Myrto Pirli
相关产品推荐
相关产品推荐

