如何使用Pytest模拟FastAPI的app.state对象以测试健康检查接口
如何使用Pytest模拟FastAPI的app.state对象以测试健康检查接口
嗨,我来帮你解决这个问题!你的测试报错AttributeError: 'State' object has no attribute 'db_client',本质是因为测试用的FastAPI实例里没有初始化app.state.db_client,而你的健康检查接口正好依赖这个对象的is_connected属性。下面给你几种实用的解决方案:
方案一:手动创建模拟对象注入到app.state
这种方式简单直接,适合快速验证测试场景。我们可以在test_app fixture里,先获取app实例,然后手动添加一个模拟的db_client到app.state中:
@pytest.fixture(scope="module") def test_app(): # 获取你的FastAPI应用实例 app = get_app() # 定义一个模拟的DB客户端类,实现接口需要的is_connected属性 class MockDBClient: is_connected = True # 默认模拟已连接状态 # 将模拟对象注入到app.state app.state.db_client = MockDBClient() # 创建TestClient并返回 client = TestClient(app) yield client
修改后再运行test_liveness,就能正常通过了,因为接口能找到app.state.db_client,且is_connected为True。
方案二:使用unittest.mock创建灵活的模拟对象
如果需要测试多种场景(比如健康/不健康状态),可以用Python标准库的unittest.mock来创建更灵活的Mock对象,方便动态修改属性值:
from unittest.mock import Mock @pytest.fixture(scope="function") def test_app_with_mock_db(): app = get_app() # 创建一个Mock对象,模拟db_client的行为 mock_db_client = Mock() mock_db_client.is_connected = True # 默认已连接 app.state.db_client = mock_db_client client = TestClient(app) # 同时返回client和mock对象,方便测试中修改属性 yield client, mock_db_client # 测试健康状态场景 def test_liveness_ready(test_app_with_mock_db): client, mock_db = test_app_with_mock_db response = client.get("/health/liveness") assert response.status_code == 200 assert response.json() == {"status": "ready"} # 测试不健康状态场景 def test_liveness_not_ready(test_app_with_mock_db): client, mock_db = test_app_with_mock_db # 动态修改mock对象的is_connected属性为False mock_db.is_connected = False response = client.get("/health/liveness") assert response.status_code == 503
这种方式能覆盖更多测试场景,验证接口在不同状态下的表现。
方案三:通过依赖注入传入模拟对象(更优雅的方式)
如果你的get_app或create_app函数支持扩展,可以修改它,允许传入db_client作为参数,这样测试时直接传入模拟对象,更符合依赖注入的设计思想:
首先修改get_app函数:
def get_app(db_client=None): settings = get_settings() config = {...} application = create_app(**config) # 如果传入了db_client,就赋值给app.state if db_client: application.state.db_client = db_client return application
然后在测试fixture中使用:
from unittest.mock import Mock @pytest.fixture(scope="module") def test_app(): # 创建模拟的db_client mock_db_client = Mock(is_connected=True) # 传入模拟对象创建app app = get_app(db_client=mock_db_client) client = TestClient(app) yield client
这种方式让测试代码和业务代码解耦,后续维护起来更方便。
备注:内容来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

