如何在 FastAPI 测试中添加依赖覆盖

How to add depedency overriding in FastAPI testing

我是 FastAPI 的新手,我已经实现了所有内容,但是在测试 API 时我无法覆盖依赖项。

这是我的代码:

test_controller.py

import pytest
from starlette.testclient import TestClient
from app.main import app
from app.core.manager_imp import ManagerImp


@pytest.fixture()
def client():
    with TestClient(app) as test_client:
        yield test_client


async def over_create_record():
    return {"msg": "inserted successfully"}

app.dependency_overrides[ManagerImp.create_record] = over_create_record

def test_post(client):
    data = {"name": "John", "email": "john@abc.com"}
    response = client.post("/person/", json=data)
    assert response.status_code == 200
    assert response.json() == {"msg": "inserted successfully"}

controller.py

from app.controllers.v1.controller import Controller
from fastapi import status, HTTPException
from app.models.taxslip import Person
from app.core.manager_imp import ManagerImp
from app.core.duplicate_exception import DuplicateException
from fastapi_utils.cbv import cbv
from fastapi_utils.inferring_router import InferringRouter

router = InferringRouter(tags=["Person"]) 
@cbv(router)
class ControllerImp(Controller):
    manager = ManagerImp()

    @router.post("/person/")
    async def create_record(self, person: Person):
        """
        Person: A person object
        returns response if the person was inserted into the database
        """
        try:
            response = await self.manager.create_record(person.dict())
            return response
        except DuplicateException as e:
            return e

manager_imp.py

from fastapi import HTTPException, status
from app.database.database_imp import DatabaseImp
from app.core.manager import Manager
from app.core.duplicate_exception import DuplicateException


class ManagerImp(Manager):
    database = DatabaseImp()
    async def create_record(self, taxslip: dict):
        try:
            response = await self.database.add(taxslip)
            return response
        except DuplicateException:
            raise HTTPException(409, "Duplicate data")

在测试中,我想覆盖 ManagerImp class 中的 create_record 函数,以便我可以获得此响应 {"msg": "inserted successfully"}。基本上,我想模拟 ManagerImp create_record 函数。正如您在 test_controller.py 中看到的那样,我已经尝试过,但我仍然得到原始回复。

我认为你应该把你的 app.dependency_overrides 放在带有 @pytest.fixture 的函数中。尝试将其放入 client().

@pytest.fixture()
def client():
    app.dependency_overrides[ManagerImp.create_record] = over_create_record
    with TestClient(app) as test_client:
        yield test_client

因为每个测试都会 运行 新鲜 app,这意味着它将重置从一个测试到另一个测试的所有内容,并且只有与 pytest 绑定的相关内容才会影响测试。

您没有使用依赖注入系统来获取 ManagerImp.create_record 函数,因此没有什么可以重写的。

由于您没有使用 FastAPI 的 Depends 来获取依赖项 - FastAPI 无法返回替代函数。

在您的情况下,您需要使用常规模拟库,例如 unittest.mock 或 pytest-mock.

我还想指出,如您在此处所做的那样初始化共享依赖项 默认情况下 将在 [=14= 的所有实例中共享同一个实例] 而不是 re-created 对于 ControllerImp.

的每个实例

The cbv decorator changes things a bit,并且如文档中所述:

For each shared dependency, add a class attribute with a value of type Depends

因此,要使其与 FastAPI 的处理方式相匹配,并使 cbv 装饰器按照您的意愿工作:

def get_manager():
    return ManagerImp()

@cbv(router)
class ControllerImp(Controller):
    manager = Depends(get_manager)

当您这样做时,您可以按计划使用 dependency_overrides

app.dependency_overrides[get_manager] = lambda: return MyFakeManager()

如果您只想替换 create_record 函数,您仍然需要使用常规模拟。

你还必须在测试完成后删除依赖覆盖,除非你希望它应用于所有测试,所以在你的夹具中使用 yield 然后在夹具开始执行时删除覆盖再次.