如何在 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
然后在夹具开始执行时删除覆盖再次.
我是 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
然后在夹具开始执行时删除覆盖再次.