Pytest中使用monkeypatch传入模拟输入测试函数遇OSError求解
解决pytest模拟输入时的OSError问题
问题场景
尝试用pytest模拟输入测试应用函数,运行测试时触发以下错误:
E OSError: pytest: reading from stdin while output is captured! Consider using
-s.
原始应用代码
class Colors: def my_colors(self): return colors_split # other stuff class Hobbies: def my_hobies(self): return hobbies_split # other stuff colors = input("Colors:") colors_split = colors.split(",").replace(" ", "") # 语法错误:列表无replace方法 hobbies = input("Hobbies:") hobbies_split = hobbies.split(",").replace(" ", "") # 同上语法错误
原始测试代码
@pytest.fixture(scope="function") def setup(monkeypatch): inputs = iter(["red", "blue", "green"]) monkeypatch.setattr('builtins.input', lambda _: next(inputs)) def test_my_colors(): colors = Colors().my_colors() assert type(colors) == list
问题原因
- 模块顶层提前执行input:应用代码里的
input()写在模块最外层,测试代码导入模块时就会立即执行这些input,而此时pytest的monkeypatchfixture还未生效,导致pytest捕获输出时尝试读取真实标准输入,引发OSError。 - 语法错误:
split()返回列表类型,列表没有replace()方法,会导致应用运行时抛出AttributeError。
修复步骤
1. 重构应用代码,延迟输入执行
将顶层的输入逻辑封装到初始化函数中,避免模块加载时自动执行:
colors_split = [] hobbies_split = [] class Colors: def my_colors(self): return colors_split # other stuff class Hobbies: def my_hobies(self): return hobbies_split # other stuff def init_app_inputs(): """初始化应用输入,需在模拟输入后调用""" global colors_split, hobbies_split colors = input("Colors:") # 修正空格处理:用列表推导式去除每个元素的前后空格 colors_split = [color.strip() for color in colors.split(",")] hobbies = input("Hobbies:") hobbies_split = [hobby.strip() for hobby in hobbies.split(",")]
2. 修改测试代码,确保monkeypatch生效后初始化输入
在测试中借助fixture,先完成input的模拟替换,再调用应用的初始化函数:
import pytest from your_app_module import Colors, init_app_inputs # 替换为实际模块名 @pytest.fixture(scope="function") def mock_input(monkeypatch): # 模拟输入顺序要和应用中input的调用顺序一致:先Colors,再Hobbies inputs = iter(["red, green, blue", "reading, hiking"]) monkeypatch.setattr('builtins.input', lambda _: next(inputs)) # 调用初始化函数,此时input已被模拟 init_app_inputs() def test_my_colors(mock_input): colors = Colors().my_colors() assert isinstance(colors, list) assert colors == ["red", "green", "blue"]
关键说明
- 把输入逻辑封装到函数中,确保只有测试主动调用时才执行,此时
monkeypatch已经替换了builtins.input,不会触发真实的标准输入读取。 - 用
strip()处理每个元素的空格,修正了列表调用replace()的语法错误。 - 模拟输入的顺序必须和应用中
input()的调用顺序严格对应。
内容的提问来源于stack exchange,提问作者user18140022
相关产品推荐
相关产品推荐

