Python单元测试:如何测试被其他函数调用的依赖函数
withdraw_cash 函数单元测试实现方案
是否需要使用mock/patch?
必须用。单元测试的核心是隔离被测逻辑,你不需要走bank_atm的完整调用链路,直接单独测试withdraw_cash即可。这个函数存在两个必须隔离的外部依赖,必须靠patch工具模拟才能完成自动化测试:
- 依赖1:内置
input()控制台输入函数,真实调用会阻塞等待人工输入,无法自动化执行,需要模拟返回不同输入值覆盖各类场景 - 依赖2:全局
user字典,测试过程中会修改余额字段,必须在每个用例执行前重置初始值,避免用例之间互相污染结果
测试覆盖场景实现
你可以用Python自带的unittest框架+unittest.mock.patch完成测试,两个校验场景的实现逻辑如下:
场景1:校验取款金额输入合法性
- 模拟
input()第一次返回非数字内容(比如"abc"),第二次返回合法数字值(避免while无限循环) - 验证函数捕获输入转换的ValueError后,正确打印输入错误提示,不会直接崩溃
场景2:校验取款金额不超过账户可用余额
- 每个用例执行前先把全局
user['balance']重置为初始值100- 子场景1:模拟输入金额大于余额(比如150),验证函数抛出余额不足的ValueError,余额不会被修改
- 子场景2:模拟输入金额小于等于余额(比如30),验证函数正常执行,余额扣减正确(剩余70)
可直接运行的测试代码
import unittest from unittest.mock import patch import io import sys # 从你的业务代码文件中导入withdraw_cash和user,例如业务代码存为atm.py则写为from atm import withdraw_cash, user class TestWithdrawCash(unittest.TestCase): def setUp(self): # 每个测试用例执行前重置余额为初始值100 user['balance'] = 100 def test_invalid_amount_input(self): # 捕获标准输出验证提示打印逻辑 captured_output = io.StringIO() sys.stdout = captured_output # 第一次返回非法输入abc,第二次返回合法值20避免无限循环 with patch('builtins.input', side_effect=['abc', '20']): withdraw_cash() sys.stdout = sys.__stdout__ output = captured_output.getvalue() # 校验错误提示正常打印 self.assertIn("Enter correct amount: ", output) # 校验合法输入后余额扣减正确 self.assertEqual(user['balance'], 80) def test_amount_exceed_balance(self): # 模拟输入150,超过初始余额100 with patch('builtins.input', return_value='150'): with self.assertRaises(ValueError) as context: withdraw_cash() # 校验错误提示文本正确 self.assertEqual(str(context.exception), "You don't have sufficient balance to make this withdrawal") # 校验余额未被异常修改 self.assertEqual(user['balance'], 100) def test_valid_withdraw(self): captured_output = io.StringIO() sys.stdout = captured_output # 模拟正常取款30 with patch('builtins.input', return_value='30'): withdraw_cash() sys.stdout = sys.__stdout__ output = captured_output.getvalue() # 校验剩余余额提示正常打印 self.assertIn("£70", output) # 校验余额扣减结果正确 self.assertEqual(user['balance'], 70) if __name__ == '__main__': unittest.main()
注意:patch的input路径要和你业务代码的模块名对应,如果业务代码文件名为
atm.py,也可以将patch路径写为atm.input,确保patch的是被测模块实际调用的input引用。
内容的提问来源于stack exchange,提问作者Jennifer
相关产品推荐
相关产品推荐

