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
相关产品推荐
相关产品推荐

