应使用 unittest.mock.patch 替换外部依赖,确保只测逻辑:patch 路径须为测试中导入路径,实例方法优先用 patch.object,异步方法需返回 AsyncMock 或协程对象,并用 async/await 测试。

怎么给带外部依赖的类方法写测试
直接测会失败——比如方法里调用了 requests.get 或读了文件,测试就变成集成测试,慢、不稳定、还可能触发真实接口。必须把外部调用“替掉”,只验证逻辑本身。
核心思路:用 unittest.mock.patch 在测试运行时临时替换目标对象,让它返回可控结果。
常见错误现象:AttributeError: __enter__(没正确 patch 类或实例方法)、AssertionError: Expected call not found(mock 没被实际调用,或 patch 路径写错)。
- patch 的路径必须是「测试代码中导入并使用的路径」,不是定义路径。例如类在
myapp.service.UserManager中定义,但你在测试文件里写了from myapp.service import UserManager,那就要 patch"myapp.test_module.UserManager.fetch_data"(假设测试文件叫test_module.py) - 如果 mock 实例方法,优先用
patch.object;mock 类方法或模块函数,用patch装饰器或上下文管理器更清晰 - 别 patch 类本身(除非真要测类创建逻辑),而是 patch 它调用的下游函数
patch.object 怎么安全地 mock 实例方法
当方法依赖另一个对象的实例方法(比如 self.db.query()),直接 patch 实例容易出错,因为实例是运行时生成的。稳妥做法是 patch 类的属性,再让实例继承该行为。
立即学习“Python免费学习笔记(深入)”;
使用场景:测试 UserService.get_user_by_id(),它内部调用 self.db.execute("SELECT ...")。
from unittest.mock import patch, MagicMock
from myapp.service import UserService
<p>def test_get_user_by_id():</p><h1>构造一个假的 db 返回值</h1><pre class="brush:php;toolbar:false;">mock_result = MagicMock()
mock_result.fetchone.return_value = (1, "alice", "alice@example.com")
with patch.object(UserService, "db") as mock_db:
mock_db.execute.return_value = mock_result
service = UserService()
user = service.get_user_by_id(1)
assert user["name"] == "alice"
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
注意:patch.object(UserService, "db") 替换的是类属性,所有该类实例都会生效;return_value 是 mock 对象的默认返回,return_value.fetchone 是链式调用的模拟。
测试异步类方法时 patch 为什么总不生效
async 方法里用 await requests_async.get() 或 await aiomysql.connect(),直接 patch("requests_async.get") 无效——因为 async 函数返回的是 coroutine 对象,不是直接结果。
关键点:mock 必须返回一个可 await 的对象,即协程函数或 AsyncMock(Python 3.8+)。
- Python 3.8+ 推荐用
AsyncMock:mock_func = AsyncMock(return_value={"id": 1}) - 老版本可用
return_value=coroutine_mock+asyncio.Future,但易出错,不建议 - 装饰器位置要对:必须 patch 测试函数中「实际 await」的那个函数,不是它内部调用的底层库
- 测试函数本身得是
async def,且用await调用被测方法
示例(Python 3.8+):
import asyncio
from unittest.mock import patch, AsyncMock
from myapp.api import DataFetcher
<p>@patch("myapp.api.httpx.AsyncClient.get")
async def test_fetch_data(mock_get):
mock_get.return_value = AsyncMock()
mock_get.return_value.json.return_value = {"status": "ok"}</p><pre class="brush:php;toolbar:false;">fetcher = DataFetcher()
result = await fetcher.fetch_latest()
assert result["status"] == "ok"
mock 后如何验证调用是否符合预期
光让方法跑通不够,还得确认它“确实按设想调用了外部服务”。assert_called_with 和 call_args 是核心验证手段。
容易忽略的细节:参数顺序、关键字参数是否传错、有没有多调用或漏调用。
-
mock_func.assert_called_once_with("https://api.example.com", timeout=5)—— 严格校验一次调用及全部参数 -
assert mock_func.call_args[0][0] == "https://api.example.com"—— 取位置参数第 0 个 -
assert mock_func.call_args.kwargs["timeout"] == 5—— 取关键字参数 - 如果方法可能被调用多次,用
assert_has_calls([call(...), call(...)], any_order=False)
特别注意:mock 对象的 call_args 是 None 直到首次被调用;没调用就查 call_args 会报 AttributeError,先用 assert_called() 做前置判断更稳妥。

















