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

如何在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_

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 07:22:43