如何在FastAPI中Mock依赖类ScopeAuthorizer
问题描述
我有一个简单的授权类:
class ScopeAuthorizer: def __init__(self, scope: str): self.scope = scope def __call__(self, request: Request): # 根据请求所需的权限范围验证请求
并定义了如下路由:
@router.post("/my_route", status_code=200) async def my_route( scope_authorized=Depends(ScopeAuthorizer(["user:write"])) ):
现在我想要测试该路由,整体Mock ScopeAuthorizer:
from fastapi.testclient import TestClient # import app class MockPermissionScopeChecker: def __init__(self, scopes): self.scopes = scopes def __call__(self, request: Request): return False class TestRoute(unittest.TestCase): def test(self): app.dependency_overrides[ScopeAuthorizer] = MockPermissionScopeChecker # 无效 app.dependency_overrides[ScopeAuthorizer('foo')] = MockPermissionScopeChecker # 也无效
我想要测试授权失败或成功时的应用行为,但无法Mock整个类。试过相关方案,但每个请求的scope会变化,没法用冻结数据类的方法。
解决方案
FastAPI的dependency_overrides是按具体依赖实例或依赖函数匹配的,直接替换类或临时创建的类实例都无法命中路由里的依赖项。这里提供两种可行的解决方法:
方法一:定位并替换路由中的具体依赖实例
直接找到路由中使用的ScopeAuthorizer(["user:write"])实例,对其进行覆盖:
from fastapi.testclient import TestClient import unittest from your_app_module import app, router, ScopeAuthorizer, Request class MockPermissionScopeChecker: def __init__(self, scopes): self.scopes = scopes def __call__(self, request: Request): # 根据测试需求返回True(授权成功)或False(授权失败) return False class TestRoute(unittest.TestCase): def setUp(self): self.client = TestClient(app) # 遍历路由找到目标依赖实例 for route in router.routes: if route.path == "/my_route" and "POST" in route.methods: for dep in route.dependencies: if isinstance(dep.dependency, ScopeAuthorizer): self.target_dep = dep.dependency break break def test_auth_failure(self): # 覆盖目标依赖实例 app.dependency_overrides[self.target_dep] = MockPermissionScopeChecker(["user:write"]) # 发起请求测试 response = self.client.post("/my_route") # 断言授权失败的结果(示例为403状态码) self.assertEqual(response.status_code, 403) # 测试后清理覆盖,避免影响其他用例 app.dependency_overrides.clear() def test_auth_success(self): # 创建返回授权成功的Mock类 class MockSuccessChecker(MockPermissionScopeChecker): def __call__(self, request: Request): return True app.dependency_overrides[self.target_dep] = MockSuccessChecker(["user:write"]) response = self.client.post("/my_route") self.assertEqual(response.status_code, 200) app.dependency_overrides.clear()
方法二:用工厂函数封装依赖,简化Mock
修改应用代码,用工厂函数创建ScopeAuthorizer实例,这样可以通过覆盖工厂函数实现统一Mock:
第一步:修改应用中的依赖定义
# 新增工厂函数 def get_scope_authorizer(scopes: list[str]): return ScopeAuthorizer(scopes) # 路由中使用工厂函数生成依赖 @router.post("/my_route", status_code=200) async def my_route( scope_authorized=Depends(lambda: get_scope_authorizer(["user:write"])) ):
第二步:测试时Mock工厂函数
class TestRoute(unittest.TestCase): def setUp(self): self.client = TestClient(app) def test_auth_failure(self): # 定义Mock工厂,返回自定义的授权检查类 def mock_get_authorizer(scopes): return MockPermissionScopeChecker(scopes) # 覆盖工厂函数 app.dependency_overrides[get_scope_authorizer] = mock_get_authorizer response = self.client.post("/my_route") self.assertEqual(response.status_code, 403) app.dependency_overrides.clear() def test_auth_success(self): def mock_get_authorizer(scopes): class MockSuccessChecker(MockPermissionScopeChecker): def __call__(self, request: Request): return True return MockSuccessChecker(scopes) app.dependency_overrides[get_scope_authorizer] = mock_get_authorizer response = self.client.post("/my_route") self.assertEqual(response.status_code, 200) app.dependency_overrides.clear()
这种方法更灵活,适合多路由使用不同权限范围的场景,无需逐个定位依赖实例。
内容的提问来源于stack exchange,提问作者mad_
相关产品推荐
相关产品推荐

