You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确创建Mock方法测试FastAPI POST接口,避免数据库写入

解决FastAPI接口单元测试中Mock DAO类方法失效的问题

问题描述

为FastAPI的POST接口编写单元测试时,期望通过Mock AnimalDAO.create_obj 避免数据库写入,但测试中Mock未生效:接口仍会向数据库写入数据,且断言create_obj被调用一次失败(实际调用0次)。

相关代码

测试代码

@pytest.mark.asyncio
async def test_animals_create(async_animals_client: AsyncClient, mocker):
    path = 'create'
    animal_data = {
        'nick_name': 'ASASDASD',
        'status': 1,
        'weight': 1.02,
        'sex': 1,
    }
    mock_create_obj = mocker.patch.object(
        AnimalDAO, 
        'create_obj', 
        return_value=None, 
        new_callable=mocker.AsyncMock,
    )

    response = await async_animals_client.post(
        url=f'{async_animals_client.base_url}{path}', 
        json=animal_data,
    )

    mock_create_obj.assert_called_once_with(fields=animal_data)
    assert response.status_code == status.HTTP_201_CREATED

接口代码

@router.post('/create', status_code=status.HTTP_201_CREATED)
async def create_animal(fields: AnimalsScheme):
    await AnimalDAO.create_obj(fields=fields.model_dump())

AnimalDAO代码

class AnimalDAO(BaseDAO):
    """DAO для модели Animals."""

    obj = Animals

BaseDAO代码

class BaseDAO:

    obj = None

    @classmethod
    async def create_obj(cls, fields: dict) -> None:
        async with async_session() as session:
            new_obj = cls.obj(
                **fields,
            )
            session.add(new_obj)
            await session.commit()

错误信息

FAILED app/animal/tests/unit/test_create_animals.py::test_animals_create - AssertionError: Expected 'create_obj' to be called once. Called 0 times.

问题原因

AnimalDAO 并未重写父类 BaseDAO 的 create_obj 类方法,调用 AnimalDAO.create_obj 时实际执行的是 BaseDAO.create_obj。直接patch AnimalDAO.create_obj 无法替换父类中的方法实现,导致Mock未生效。

解决方案

方案一:patch父类BaseDAO的create_obj方法

修改测试中的patch逻辑,直接patch父类的类方法,这样所有继承BaseDAO的子类调用该方法时都会触发Mock:

from unittest.mock import ANY
from app.path.to.base_dao import BaseDAO  # 替换为实际的BaseDAO导入路径

@pytest.mark.asyncio
async def test_animals_create(async_animals_client: AsyncClient, mocker):
    path = 'create'
    animal_data = {
        'nick_name': 'ASASDASD',
        'status': 1,
        'weight': 1.02,
        'sex': 1,
    }
    # patch BaseDAO的create_obj方法
    mock_create_obj = mocker.patch.object(
        BaseDAO, 
        'create_obj', 
        return_value=None, 
        new_callable=mocker.AsyncMock,
    )

    response = await async_animals_client.post(
        url=f'{async_animals_client.base_url}{path}', 
        json=animal_data,
    )

    # 类方法会自动传入cls参数,用ANY匹配任意cls值
    mock_create_obj.assert_called_once_with(ANY, fields=animal_data)
    assert response.status_code == status.HTTP_201_CREATED

方案二:在AnimalDAO中显式重写create_obj方法

如果希望直接patch AnimalDAO 的方法,可以在AnimalDAO中显式重写create_obj,使其调用父类方法,之后再patch AnimalDAO.create_obj:

修改AnimalDAO代码

class AnimalDAO(BaseDAO):
    """DAO для модели Animals."""

    obj = Animals

    @classmethod
    async def create_obj(cls, fields: dict) -> None:
        await super().create_obj(fields)

修改测试代码

@pytest.mark.asyncio
async def test_animals_create(async_animals_client: AsyncClient, mocker):
    path = 'create'
    animal_data = {
        'nick_name': 'ASASDASD',
        'status': 1,
        'weight': 1.02,
        'sex': 1,
    }
    mock_create_obj = mocker.patch.object(
        AnimalDAO, 
        'create_obj', 
        return_value=None, 
        new_callable=mocker.AsyncMock,
    )

    response = await async_animals_client.post(
        url=f'{async_animals_client.base_url}{path}', 
        json=animal_data,
    )

    # 此时断言无需考虑cls参数,因为重写后的方法直接接收fields
    mock_create_obj.assert_called_once_with(fields=animal_data)
    assert response.status_code == status.HTTP_201_CREATED

额外注意事项

  • 确保测试中导入的AnimalDAO/BaseDAO与接口代码中使用的是同一个模块的类,避免因导入路径不一致导致类对象不匹配。
  • 检查AnimalsScheme.model_dump()的输出是否与测试中的animal_data完全一致,若存在默认字段(如id),需在测试数据中补充或使用ANY匹配参数。

内容的提问来源于stack exchange,提问作者Apple

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 11:35:55