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

FastAPI+Tortoise-ORM接口测试遇MagicMock await错误求解决

问题描述

我用tortoise-orm和FastAPI开发应用,编写了/status/接口:

@app.get("/status/", response_model=list[Status])
async def get_status() -> list[Status]:
    """
    返回代表过滤消息历史/状态的Status对象列表。

    返回:
        list[Status]: 代表过滤消息历史的Status对象列表。
    """
    history = await History.all()
    return list(map(Status.from_orm, history))

尝试用unittest测试该接口,代码如下:

import unittest
from unittest.mock import AsyncMock, patch

from fastapi.testclient import TestClient

from source.craft.models import History
from source.main import app


class TestAPI(unittest.TestCase):

    def setUp(self) -> None:
        self.client = TestClient(app)

    @patch("source.craft.models.History.all")
    def test_get_status(self, history_mock):
        object_mock = AsyncMock()
        object_mock.return_value = [
            History(id=1, text="Hello!", filter="lower", url=None, status="Done"),
        ]
        history_mock.return_value.__aenter__.return_value = object_mock

        response = self.client.get("/status")

        self.assertEqual(response.status_code, 200)

但测试失败,报错:

>       history = await History.all()
E       TypeError: object MagicMock can't be used in 'await' expression
错误原因

你错误地给History.all()的返回值模拟了上下文管理器(__aenter__),但Tortoise-ORM的all()本身是异步方法,调用后返回可await的QuerySet对象,直接await就能拿到结果列表,不需要进入上下文。你的mock方式让History.all()返回普通MagicMock对象,无法被await,因此触发报错。

正确的测试代码

直接让History.all()这个mock对象返回可await的预设数据即可,有两种简洁写法:

写法一:直接指定AsyncMock的返回值

import unittest
from unittest.mock import AsyncMock, patch

from fastapi.testclient import TestClient

from source.craft.models import History
from source.main import app


class TestAPI(unittest.TestCase):

    def setUp(self) -> None:
        self.client = TestClient(app)

    @patch("source.craft.models.History.all")
    def test_get_status(self, history_mock):
        # 让all()异步方法直接返回预设的History列表
        history_mock.return_value = AsyncMock(return_value=[
            History(id=1, text="Hello!", filter="lower", url=None, status="Done"),
        ])

        response = self.client.get("/status")

        self.assertEqual(response.status_code, 200)
        # 可选:验证返回内容是否符合预期
        self.assertEqual(len(response.json()), 1)
        self.assertEqual(response.json()[0]["text"], "Hello!")
        # 可选:验证方法是否被正确调用
        history_mock.assert_called_once()

写法二:将mock对象转为AsyncMock类型

import unittest
from unittest.mock import AsyncMock, patch

from fastapi.testclient import TestClient

from source.craft.models import History
from source.main import app


class TestAPI(unittest.TestCase):

    def setUp(self) -> None:
        self.client = TestClient(app)

    @patch("source.craft.models.History.all")
    def test_get_status(self, history_mock):
        # 把默认的MagicMock转为AsyncMock,直接设置返回值
        history_mock.__class__ = AsyncMock
        history_mock.return_value = [
            History(id=1, text="Hello!", filter="lower", url=None, status="Done"),
        ]

        response = self.client.get("/status")

        self.assertEqual(response.status_code, 200)
        history_mock.assert_called_once()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 15:45:37