直接调用含input()的函数会卡住测试,因input()默认从sys.stdin读取而测试无终端输入;需用pytest的monkeypatch将sys.stdin替换为带换行符的io.stringio对象。

为什么直接调用含 input() 的函数会卡住测试
因为 input() 默认从 sys.stdin 读取,运行测试时没有真实终端输入,程序就挂起等待——这不是 bug,是设计行为。你不能靠手动敲回车来跑 CI,必须把 stdin 替换成可控的输入流。
用 pytest + monkeypatch.setattr() 注入模拟输入
核心思路:把 sys.stdin 替换成一个带预设内容的 io.StringIO 对象,并确保它支持 readline()(input() 内部调用的就是这个)。
- 必须用
io.StringIO,不能用普通字符串或列表;input()要求对象有readline()方法 - 每行输入末尾要带换行符
\n,否则input()会继续等 -
monkeypatch是 pytest 内置 fixture,作用域为当前测试函数,无需手动清理
import io
import sys
<p>def test_greet_user(monkeypatch):</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill7657" title="python 查询技能"><img
src="https://img.php.cn/upload/skill/000/000/081/179161384046229.jpg" alt="python 查询技能" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill7657" title="python 查询技能" class="overflowclass">python 查询技能</a>
<p class="overflowclass">查询客流数据,输出JSON格式,可直接导入Bitable等可视化工具</p>
</div>
<a rel="nofollow" href="/xiazai/skill7657" title="python 查询技能" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><h1>模拟用户依次输入 "Alice" 和 "25"</h1><pre class="brush:python;toolbar:false;">inputs = io.StringIO("Alice\n25\n")
monkeypatch.setattr(sys, "stdin", inputs)
result = greet_user() # 假设该函数两次调用 input()
assert result == "Hello, Alice! You are 25 years old."
处理多轮 input() 时容易漏掉的细节
常见错误是只写 "Alice25" 或 "Alice\n25"(缺末尾换行),导致第二次 input() 阻塞或读到空字符串。
- 每次输入都需独立一行:
"Alice\n25\nyes\n",哪怕最后一行也建议加\n - 如果函数内部用了
input().strip(),模拟输入里的空格和换行仍需保留——strip()是在读取后才处理的 - 避免用
monkeypatch.setattr(builtins, "input", ...):虽然可行,但会覆盖全局input,干扰同一模块中其他未 mock 的测试
当函数捕获 EOFError 或需要空输入时怎么模拟
比如用户按 Ctrl+D(Unix)或 Ctrl+Z(Windows)终止输入,Python 会抛 EOFError。这时不能靠 StringIO,得 mock input 函数本身。
- 用
monkeypatch.setattr("builtins.input", lambda: (_ for _ in ()).throw(EOFError))模拟首次调用就 EOF - 更清晰的做法是定义临时函数:
def raise_eof(): raise EOFError,再monkeypatch.setattr("builtins.input", raise_eof) - 若需前几次正常、最后一次 EOF,可用计数器控制:
it = iter(["foo", "bar"]); lambda: next(it, (_ for _ in ()).throw(EOFError))
交互逻辑越复杂,越要靠近实际调用点 mock —— 直接动 sys.stdin 简单可靠,但遇到异常流或混合读取(如同时用 sys.stdin.readline() 和 input())时,mock input 更精准。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










