[英]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.