[英]How to add depedency overriding in FastAPI testing
I'm new to FastAPI, I have implemented everything but when it comes to testing the API I can't override a dependency.我是 FastAPI 的新手,我已经实现了所有内容,但是在测试 API 时我无法覆盖依赖项。
Here is my code:这是我的代码:
test_controller.py测试控制器.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 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 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")
In testing I want to override create_record function from ManagerImp class so that I could get this response {"msg": "inserted successfully"}
.在测试中,我想覆盖 ManagerImp class 中的 create_record function 以便我可以获得此响应{"msg": "inserted successfully"}
。 Basically, I want to mock ManagerImp create_record function. I have tried as you can see in test_controller.py
but I still get the original response.基本上,我想模拟 ManagerImp create_record function。正如您在test_controller.py
中看到的那样,我已经尝试过,但我仍然得到原始响应。
You're not using the dependency injection system to get the ManagerImp.create_record
function, so there is nothing to override.您没有使用依赖项注入系统来获取ManagerImp.create_record
function,因此无需覆盖任何内容。
Since you're not using FastAPI's Depends
to get your dependency - FastAPI has no way of returning the alternative function.由于您没有使用 FastAPI 的Depends
来获取依赖项 - FastAPI 无法返回替代项 function。
In your case you'll need to use a regular mocking library instead, such as unittest.mock or pytest-mock .在您的情况下,您需要使用常规的 mocking 库,例如 unittest.mock 或pytest-mock 。
I'd also like to point out that initializing a shared dependency as in you've done here by default will share the same instance across all instances of ControllerImp
instead of being re-created for each instance of ControllerImp
.我还想指出,默认情况下初始化共享依赖项将跨所有ControllerImp
实例共享同一个实例,而不是为每个ControllerImp
实例重新创建。
The cbv decorator changes things a bit , and as mentioned in the documentation: cbv 装饰器稍微改变了一些东西,如文档中所述:
For each shared dependency, add a class attribute with a value of type
Depends
对于每个共享依赖项,添加一个值为Depends
类型的 class 属性
So to get this to match the FastAPI way of doing things and make the cbv
decorator work as you want to:因此,要使其与 FastAPI 的做事方式相匹配,并使cbv
装饰器按照您的意愿工作:
def get_manager():
return ManagerImp()
@cbv(router)
class ControllerImp(Controller):
manager = Depends(get_manager)
And when you do it this way, you can use dependency_overrides
as you planned:当你这样做时,你可以按照你的计划使用dependency_overrides
:
app.dependency_overrides[get_manager] = lambda: return MyFakeManager()
If you only want to replace the create_record
function, you'll still have to use regular mocking.如果您只想替换create_record
function,您仍然必须使用常规 mocking。
You'll also have to remove the dependency override after the test has finished unless you want it to apply to all tests, so use yield
inside your fixture and then remove the override when the fixture starts executing again.您还必须在测试完成后删除依赖覆盖,除非您希望它应用于所有测试,因此请在您的夹具中使用yield
,然后在夹具再次开始执行时删除覆盖。
I think you should put your app.dependency_overrides
inside the function with @pytest.fixture
.我认为您应该将app.dependency_overrides
放在带有@pytest.fixture
的 function 中。 Try to put it inside your client()
.尝试将其放入您的client()
中。
@pytest.fixture()
def client():
app.dependency_overrides[ManagerImp.create_record] = over_create_record
with TestClient(app) as test_client:
yield test_client
because every test will run the fresh app
, meaning it will reset everything from one to another test and only related things bind with the pytest
will effect the test.因为每个测试都会运行新的app
,这意味着它将重置从一个测试到另一个测试的所有内容,并且只有与pytest
绑定的相关内容才会影响测试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.