Home > Back-end >  How to add depedency overriding in FastAPI testing
How to add depedency overriding in FastAPI testing

Time:04-12

I'm new to FastAPI, I have implemented everything but when it comes to testing the API I can't override a dependency.

Here is my code:

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": "[email protected]"}
    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")

In testing I want to override create_record function from ManagerImp class so that I could get this response {"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.

CodePudding user response:

You're not using the dependency injection system to get the ManagerImp.create_record function, so there is nothing to override.

Since you're not using FastAPI's Depends to get your dependency - FastAPI has no way of returning the alternative function.

In your case you'll need to use a regular mocking library instead, such as unittest.mock or 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.

The cbv decorator changes things a bit, and as mentioned in the documentation:

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

So to get this to match the FastAPI way of doing things and make the cbv decorator work as you want to:

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:

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.

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.

CodePudding user response:

I think you should put your app.dependency_overrides inside the function with @pytest.fixture. Try to put it inside your 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.

  • Related