
本文详解如何使用 unittest.mock.patch 正确替换模块级全局对象(如已实例化的类),重点纠正常见误区:patch 必须作用于被测函数的调用上下文,而非 setUp 中提前 mock。
本文详解如何使用 `unittest.mock.patch` 正确替换模块级全局对象(如已实例化的类),重点纠正常见误区:patch 必须作用于被测函数的调用上下文,而非 `setup` 中提前 mock。
在 Python 单元测试中,对全局变量(尤其是已初始化的类实例)进行 patch 是常见需求,但极易因 patch 位置或目标不准确而失败。以你的项目结构为例:
├── core
| └── service
| └── file.py # 定义 cls_instance = myClass() 和 fun()
└── tests
└── test_file.py # 测试 fun()
关键问题在于:你试图在 setUp() 方法上使用 @patch("core.service.file.cls_instance"),这会导致 mock 在 fun() 执行前已被销毁或未生效——因为 setUp() 是测试方法执行前的准备阶段,而 @patch 装饰器绑定的 mock 生命周期仅覆盖其所在方法体;当 test_fun() 运行时,cls_instance 已恢复为原始实例,导致 fun() 实际调用的是真实 request() 方法(返回 None),而非 mock 行为。
✅ 正确做法是:将 @patch 直接应用于被测函数 test_fun,并接收 mock 参数,确保 patch 在 fun() 调用期间生效:
创建 Flask + Vue.js 全栈项目,采用单端口架构、uv 依赖管理、结构化日志和生产级前端设计。当用户要求以下操作时触发:创建新的 Flask+Vue 项目、搭建 Web 应用、构建全栈 Python+Vue 应用、初始化 Flask Web 项目、设置单端口 Flask 前后端项目。触发词:新建项目、创建项目、搭建项目、Flask项目、Vue项目。
import unittest
from unittest.mock import patch
from core.service.file import fun
class MyTestCase(unittest.TestCase):
@patch("core.service.file.cls_instance")
def test_fun(self, mock_instance):
# 配置 mock:让 cls_instance.request() 返回 1
mock_instance.request.return_value = 1
# 调用被测函数
resp = fun()
# 断言结果
self.assertEqual(resp, 1)
# (可选)验证 request 是否被调用过一次
mock_instance.request.assert_called_once()
⚠️ 注意事项:
-
Patch 路径必须准确:
"core.service.file.cls_instance"指的是file.py模块中定义的全局变量名cls_instance,而非myClass类本身; -
不要 patch 类,要 patch 实例:你希望拦截的是
cls_instance.request()调用,因此需 mock 该实例对象,而非myClass或another_module.myClass; -
避免在
setUp中 patch:setUp中的@patch不会自动延续到后续测试方法,且容易引发作用域混乱; -
推荐使用
patch.object提升可读性(可选):from core.service import file @patch.object(file, 'cls_instance') def test_fun(self, mock_instance): ...
总结:Patch 全局对象的核心原则是——在被测函数执行的同一命名空间内生效。将 @patch 放在测试方法上、正确指定模块内变量路径、并通过参数接收 mock 实例,即可可靠隔离外部依赖,让单元测试真正聚焦于逻辑验证。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










