如何对含input()和print()循环的函数进行单元测试(含pytest方案)
优化Python函数的输入输出测试方案(unittest + pytest)
待测试的Python函数:
def welcome(): for i in range(2): # 实际代码中是range(100) name = input("enter your name") print("welcome " + name)
一、unittest优化方案
你之前的代码核心问题是装饰器参数顺序搞反了:@mock.patch是从下往上生效的,所以被装饰的测试方法参数顺序应该是最后一个装饰器的mock对象在前。另外可以用assert_has_calls一次性验证所有print调用,简化断言逻辑。
优化后的代码:
import unittest from unittest import mock from unittest.mock import call import welcome class WelcomeTests(unittest.TestCase): @mock.patch('builtins.print') @mock.patch('welcome.input') def test_welcome_with_multiple_inputs(self, mocked_input, mock_print): # 设置模拟输入的序列 mocked_input.side_effect = ["Alice", "Bob"] # 执行待测试函数 welcome.welcome() # 验证print的所有调用(顺序和内容都要匹配) mock_print.assert_has_calls([ call("welcome Alice"), call("welcome Bob") ], any_order=False) if __name__ == '__main__': unittest.main()
关键说明:
- 调整
@mock.patch顺序,确保测试方法参数和装饰器顺序对应(最后装饰的mock先作为参数传入) - 使用
unittest.mock.call构建预期的调用对象,配合assert_has_calls验证多次调用的顺序和内容 - 去掉不必要的调试代码,让测试逻辑更清晰
二、pytest实现方案
用pytest的monkeypatch fixture可以更简洁地模拟输入和捕获输出:
import pytest import welcome def test_welcome_with_multiple_inputs(monkeypatch): # 模拟input的返回值序列 inputs = ["Alice", "Bob"] monkeypatch.setattr('builtins.input', lambda _: inputs.pop(0)) # 捕获print的输出 printed_lines = [] monkeypatch.setattr('builtins.print', lambda x: printed_lines.append(x)) # 执行待测试函数 welcome.welcome() # 断言输出内容 assert printed_lines == ["welcome Alice", "welcome Bob"]
关键说明:
- 用
monkeypatch.setattr替换input,每次调用返回序列中的下一个值 - 替换
print为一个收集输出的函数,把每次打印的内容存入列表 - 直接断言收集到的输出列表和预期一致,逻辑直观易懂
内容的提问来源于stack exchange,提问作者chikitin
相关产品推荐
相关产品推荐

