pv250021
pv250021

Reputation: 115

FastAPI mocking dependencies

I am writing unit test cases for my fastapi project and unable to mock a dynamodb call.

File_1

This file has all the methods to perform DynamoDB actions using boto3 calls.

class DynamoDBRepository:

  1. Insert Item - Inserts value
  2. Get Item - Returns value

#File_2

Has a "AppConfig" class which will be used as a dependency in a later file


from file_1 import DynamoDBRepository 

class AppConfig:

    def __init__(self) -> None:
        """Constructor class to instantiate dynamodb"""
        self._job_table = "Dynamo_DB_Table"
        self._region = "Table_region"
        self._dynamodb_repository = DynamoDBRepository(table=self._job_table, region=self._region) # created a object for the dynamodb class mentioned in file 1.

File_3:

This file has the fast_api route decorator

from file_2 import AppConfig
@router.get(
    "/object/{object_id}"
)
def get_request(
    object_id: str,
    objects: AppConfig = Depends(AppConfig),
) -> ObjectBody:

    try:
        object_detail = objects._dynamodb_repository.get_item({"object_id": object_id})
        return object_detail["Item"]


I am trying to mock the get_item method in my test file:

File_4

This is my test file in which

client = TestClient(fast_api_app)

class MockAppConfig:


    def __init__(self) -> None:
        """Constructor class to instantiate dynamodb and lambda"""
         self._job_table = "Dynamo_DB_Table"
        self._region = "Table_region"
        self._dynamodb_repository = DynamoDBRepository(table=self._job_table, region=self._region)


def test_get_request():
    fast_api_app.dependency_overrides[AppConfig] = MockAppConfig
    MockAppConfig()._dynamodb_repository.get_item = {
         "id": "1234",
         "title": "Foo",
         "description": "Hello",
     }

        response = client.get("/objects/1234")
        assert response.status_code == 200
        assert response.json() == {
            "id": "foo",
            "title": "Foo",
            "description": "Hi",
        }

The mocking of get_item is not working and it is still querying the original db and failing due to credential check.

I tried monkeypatch & fastapi_dep fixtures, also patching but somehow the get_item method mocking isn't working

Upvotes: 1

Views: 5509

Answers (2)

Peter K
Peter K

Reputation: 2474

Building on @svfat's answer, here is how you can do the test with fastapi_dep - pick any one of the test approaches - with clause or indirect parameter:

class MockDynamoDBRepository():
    def __init__(self, *args, **kwargs):
        pass

    def get_item(self, *args, **kwargs):
        return {
            "Item": {
                "id": "foo",
                "title": "Foo",
                "description": "Hi",
            }
        }


class MockAppConfig:

    def __init__(self) -> None:
        """Constructor class to instantiate dynamodb and lambda"""
        self._job_table = "Mock Dynamo_DB_Table"
        self._region = "Mock Table_region"
        self._dynamodb_repository = MockDynamoDBRepository(table=self._job_table,
                                                       region=self._region)


def test_get_request_deps(fastapi_dep):
    with fastapi_dep(fast_api_app).override(
        {
            AppConfig: MockAppConfig,
        }
    ):
        response = client.get("/objects/1234")
        assert response.status_code == 200
        assert response.json() == {
            "id": "foo",
            "title": "Foo",
            "description": "Hi",
        }


@pytest.mark.parametrize(
    "fastapi_dep",
    [
        (
            fast_api_app,
            {AppConfig: MockAppConfig},
        )
    ],
    indirect=True,
)
def test_get_request_deps_indirect(fastapi_dep):
    response = client.get("/objects/1234")
    assert response.status_code == 200
    assert response.json() == {
        "id": "foo",
        "title": "Foo",
        "description": "Hi",
    }

If you don't want to create all the extra classes, you can use the pure mock approach like so:

from mock.mock import MagicMock

def test_get_request_deps_mock(fastapi_dep):
    my_mock = MagicMock()
    my_mock._dynamodb_repository.get_item.return_value = {
        "Item": {
            "id": "foo",
            "title": "Foo",
            "description": "Hi",
        }
    }
    with fastapi_dep(file_3.app).override(
        {
            AppConfig: lambda: my_mock,
        }
    ):
        response = client.get("/objects/1234")
        assert response.status_code == 200
        assert response.json() == {
            "id": "foo",
            "title": "Foo",
            "description": "Hi",
        }

Upvotes: 1

svfat
svfat

Reputation: 3363

Will mocking get_item method work?

class MockDynamoDBRepository():
     def get_item(*args, **kwargs):
         return {
             "Item": {
             "id": "foo",
             "title": "Foo",
             "description": "Hi",
             }
         }

class MockAppConfig:
    def __init__(self) -> None:
        """Constructor class to instantiate dynamodb and lambda"""
         self._job_table = "Dynamo_DB_Table"
        self._region = "Table_region"
        self._dynamodb_repository = MockDynamoDBRepository(table=self._job_table, region=self._region)

def test_get_request():
    fast_api_app.dependency_overrides[AppConfig] = MockAppConfig

    response = client.get("/objects/1234")
    assert response.status_code == 200
    assert response.json() == {
            "id": "foo",
            "title": "Foo",
            "description": "Hi",
        }

Upvotes: 2

Related Questions