簡體   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