繁体   English   中英

如何在测试期间避免 FastAPI 中的身份验证依赖

[英]How to avoid authentication dependencies in FastAPI during testing

这可能是一个新手问题,但我无法让dependency_overrides用于测试。
按照文档,这应该很容易实现,但我遗漏了一些东西......
考虑以下代码:

在 main.py 中:

from fastapi import FastAPI
from routes import router

app = FastAPI()
app.include_router(router)

在 routes.py 中:

from fastapi import APIRouter, status
from fastapi.param_functions import Depends
from fastapi.responses import JSONResponse
from authentication import Authentication

router = APIRouter()

@router.get("/list/", dependencies=[Depends(Authentication(role=user))])
async def return_all():
    response = JSONResponse(
        status_code=status.HTTP_200_OK,
        content="Here is all the objects!"
    )
    return response

在 test_list.py 中:

from unittest import TestCase
from fastapi.testclient import TestClient
from main import app
from authentication import Authentication

def override_dependencies():
    return {"Some": "Thing"}

client = TestClient(app)
app.dependency_overrides[Authentication] = override_dependencies

class ListTestCase(TestCase):
    def test_list_get(self):
        response = client.get("/list/")
        self.assertEqual(200, response.status_code)

给出以下错误:

self.assertEqual(200, response.status_code)
AssertionError: 200 != 403

即,它试图进行身份验证但被拒绝。 因此,它似乎并没有覆盖我的依赖。

请注意,路径操作装饰器( @router.get )中使用了 Depends,而不是文档中的函数……

我看到您正在使用Authentication(role=user)作为依赖项,但随后您尝试覆盖Authentication ,这是两个不同的可调用对象,前者实际上是Authentication(role=user).__call__ 因此我猜 FastAPI 无法匹配正确的覆盖。

这种方法的问题是Authentication(role=user).__call__是一个实例方法,但是为了覆盖 dependency_overrides 中的dependency_overrides项,您必须能够静态地处理可调用对象,以便每次调用时它总是相同的它。

我必须实现一个类似的东西,我通过像这样注入身份验证逻辑来​​解决这个问题:

class RestIdentityValidator:
    methods: List[AuthMethod]

    def __init__(self, *methods: AuthMethod):
        self.methods = list(dict.fromkeys([e for e in AuthMethod] if methods is None else methods))

    def __call__(
            self,
            bearer_token_identity: IdentityInfo | None = Depends(get_bearer_token_identity),
            basic_token_identity: IdentityInfo | None = Depends(get_basic_token_identity)
    ) -> IdentityInfo | None:
        identity = None
        for auth_method in self.methods:
            match auth_method:
                case AuthMethod.BEARER:
                    identity = bearer_token_identity
                case AuthMethod.BASIC:
                    identity = basic_token_identity
                case _:
                    pass
            if identity is not None:
                break
        return identity

然后像这样使用它:

@router.get("/users", response_model=Response[List[UserOut]])
async def get_all_users(
        identity: IdentityInfo = Depends(RestIdentityValidator(AuthMethod.BEARER)),
...

然后你可以正常注入和覆盖它。 在这种情况下,您在装饰器中使用它的事实不应该有任何区别。

现在这个示例没有实现与您相同的功能,但我希望它可以为您提供有关如何解决问题的提示。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM