应 patch 被测模块中的 input 名称绑定,而非 builtins.input;因 Python 查找 input 时依赖当前模块全局命名空间,正确做法是 @patch('mymodule.input') 或 patch.object(mymodule, 'input')。

直接用 unittest.mock.patch 覆盖内置函数的引用即可,但必须注意作用域和替换目标——不是 patch input 字符串,而是 patch 它在被测模块中实际被导入/调用的位置。
为什么 patch builtins.input 常常不生效?
Python 的 input 在不同 Python 版本下归属不同模块:__builtin__(Python 2)或 builtins(Python 3)。但关键问题不在这里——而在于 patch 的目标必须是「被测代码从哪里访问 input」。如果被测函数写的是 input(...),它实际查找的是当前模块的全局命名空间,而该命名空间里的 input 来自 builtins,所以你要 patch 的是「被测模块的 input 名字绑定」,不是 builtins.input 本身。
- 错误做法:
@patch('builtins.input')→ 只有当被测代码显式写了from builtins import input才可能生效 - 正确做法:
@patch('mymodule.input')(假设被测函数在mymodule.py中) - 更稳妥写法:用
patch.object直接替换模块的__builtins__字典项(见下条)
用 patch.object 替换模块级 input 更可靠
绕过路径猜测,直接操作被测模块的 __builtins__ 引用,兼容性更好,也避免因相对导入、重命名等导致 patch 失效。
from unittest.mock import patch, Mock
import mymodule
<p>def test_user_input():</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill4102" title="Shadows Python Sensei"><img
src="https://img.php.cn/upload/skill/000/000/081/178990406882325.jpg" alt="Shadows Python Sensei" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill4102" title="Shadows Python Sensei" class="overflowclass">Shadows Python Sensei</a>
<p class="overflowclass">Python 最佳实践助手——代码规范、设计模式、性能优化、测试与类型注解。适用于编写或审查 Python 代码。</p>
</div>
<a rel="nofollow" href="/xiazai/skill4102" title="Shadows Python Sensei" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><h1>模拟 input 返回固定字符串</h1><pre class="brush:php;toolbar:false;">mock_input = Mock(return_value="yes")
with patch.object(mymodule, 'input', mock_input):
result = mymodule.ask_continue() # 假设该函数内调用了 input()
assert result is True
mock_input.assert_called_once_with("Continue? (y/n): ")
- 必须传入被测函数所在的模块对象(
mymodule),不是字符串名 -
mock_input可设return_value或side_effect(如返回不同值序列) - 若被测函数在类方法里调用
input,仍需 patch 所在模块,不是类本身
如何同时 mock print 并捕获输出内容?
mock print 不是为了“让它不输出”,而是为了验证是否按预期调用了它——比如检查提示文案、参数格式或调用次数。
- 用
patch('builtins.print')是安全的,因为所有模块默认都通过builtins访问print - 捕获输出更推荐用
io.StringIO+sys.stdout重定向,适合断言完整输出字符串 - 但若只需验证 print 是否被调用、传了什么参数,mock 更轻量、语义更清晰
from unittest.mock import patch
<p>def test_greet():
with patch('builtins.print') as mock_print:
mymodule.greet("Alice")
mock_print.assert_called_once_with("Hello, Alice!")
</p>
真实项目中容易忽略的边界点
mock 内置函数时,最容易漏掉的是多线程、异步协程或子进程场景:patch 只影响当前线程的模块命名空间;如果被测逻辑 spawn 了新进程,input 或 print 会走真实实现。
- 交互式 CLI 工具测试时,别只 mock 一次
input——用side_effect=[...]提供输入序列 - 若被测代码做了
from builtins import input as get_input,你必须 patchmymodule.get_input,而不是input - pytest 用户注意:
patch装饰器顺序影响作用域,优先用 fixture 方式(monkeypatch.setattr)更直观
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










