如何测试调用含输入交互函数的Python业务功能?
一、最快解决:直接模拟confirm_choice的返回值
既然你已经完成了confirm_choice的单元测试,测试delete_booking时完全不需要关心它内部怎么获取用户输入——只需要验证当confirm_choice返回True/False时,delete_booking的行为是否符合预期即可。
假设你的代码结构如下:
# 模块:booking_ops.py def confirm_choice(): while True: resp = input("确认删除?(Y/N): ").strip().upper() if resp == 'Y': return True elif resp == 'N': return False def delete_booking(booking_id): if confirm_choice(): # 执行CSV删除逻辑 print(f"删除预订{booking_id}") return True else: print("取消删除") return False
测试时用unittest.mock.patch直接替换delete_booking调用的confirm_choice:
import unittest from unittest.mock import patch from booking_ops import delete_booking class TestDeleteBooking(unittest.TestCase): @patch('booking_ops.confirm_choice') def test_delete_confirmed(self, mock_confirm): # 模拟用户确认操作 mock_confirm.return_value = True result = delete_booking("BK-001") self.assertTrue(result) # 可选:断言确认函数被调用过一次 mock_confirm.assert_called_once() @patch('booking_ops.confirm_choice') def test_delete_canceled(self, mock_confirm): # 模拟用户取消操作 mock_confirm.return_value = False result = delete_booking("BK-001") self.assertFalse(result) mock_confirm.assert_called_once() if __name__ == '__main__': unittest.main()
注意:patch的路径必须是delete_booking实际引用confirm_choice的路径——如果两者在同一个模块,路径就是模块名+函数名;如果delete_booking是从其他模块导入的confirm_choice,要patchdelete_booking所在模块里的confirm_choice引用,而非原定义模块的函数。
二、为什么直接mock input会失效?
如果之前你尝试patchbuiltins.input但没生效,核心原因是confirm_choice内部的input引用没被正确覆盖:比如confirm_choice在另一个模块,你却patch了测试模块里的input,此时confirm_choice调用的还是原模块的input,自然需要手动输入。这种情况下要么patchconfirm_choice所在模块的input,要么直接patchconfirm_choice本身(后者更简洁)。
三、是否需要解耦函数?
解耦是更优的长期实践,推荐采用。比如把confirm_choice作为可选参数传入delete_booking,测试时直接传入模拟逻辑,无需依赖mock工具:
修改业务函数:
def delete_booking(booking_id, confirm_func=confirm_choice): if confirm_func(): # 执行删除逻辑 return True else: return False
测试代码可以简化为:
def test_delete_confirmed(): result = delete_booking("BK-001", lambda: True) assert result is True def test_delete_canceled(): result = delete_booking("BK-001", lambda: False) assert result is False
这种方式让依赖关系更明确,测试代码更直观,也避免了mock路径错误的问题。
内容的提问来源于stack exchange,提问作者bjornelvar

