如何为需用户输入与随机数的函数编写测试驱动函数?
嘿,作为刚走完CS入门阶段的过来人,太懂你这种困惑了——既要处理没完没了的用户输入,又要搞定随机数这种“不可控”的东西,测试起来感觉无从下手!别慌,咱们用最适合新手的方法一步步拆解,不用复杂的工具,就能把测试搞定。
第一步:把用户输入“管起来”——模拟输入的简单方法
你之前想让计算机模拟用户输入,其实不用搞复杂的库,只要把原来用input()的地方改成一个可以接受预设输入的函数就行。比如原来直接写input("下注金额: "),现在换成一个自定义函数:
def get_user_input(prompt, test_inputs=None): # 如果传入了test_inputs列表,就从里面拿第一个值 if test_inputs is not None: return test_inputs.pop(0) # 没传的话,就用真实的用户输入 return input(prompt)
这样在测试的时候,你可以提前把要模拟的输入列成一个列表,比如["10", "h", "y", "5", "l", "n"],传给游戏函数,它就会依次用这些值代替手动输入,完全自动化!
第二步:驯服随机数——让它每次都一样
随机数的麻烦在于每次运行结果不同,测试没法复现。入门级的解决方案就是固定随机种子,或者把生成随机数的逻辑抽出来方便替换。
方法1:固定随机种子
比如在Python里,random.seed(数字)可以让随机数生成器每次都输出相同的序列。比如你写:
import random def test_game(): random.seed(42) # 固定种子,每次运行随机数都一模一样 # 然后运行游戏,模拟输入,检查结果
方法2:抽离随机数生成函数(更灵活)
把生成随机牌的逻辑单独写成一个函数,测试的时候换成固定返回值的“假函数”:
# 正常生成随机牌的函数 def generate_random_card(): return random.randint(1, 13) # 测试用的假函数,固定返回7 def mock_generate_card(): return 7
然后在你的游戏函数里,允许传入这个生成函数作为参数:
def high_low_game(card_generator=generate_random_card): # 生成牌的时候用card_generator()代替直接调用random.randint current_card = card_generator()
测试的时候,就传mock_generate_card,这样牌的数值完全由你控制,测试起来超方便!
第三步:先拆小功能,再测整体——从单元测试开始
教授给的updateBalance测试就是单元测试的思路:先把游戏里的小功能单独拆出来测试,比如余额更新、输赢判断,等这些小功能都测对了,再整合测试整个游戏流程。
比如先测试updateBalance:
def test_update_balance(): # 测试赢的情况:初始100,下注20,赢了变120 assert update_balance(100, 20, win=True) == 120 # 测试输的情况:初始100,下注20,输了变80 assert update_balance(100, 20, win=False) == 80 # 测试平局:余额不变 assert update_balance(100, 20, win=None) == 100 print("余额更新测试通过!")
再测试输赢判断的函数:
def test_judge_win(): # 用户选高,下一张牌更大→赢 assert judge_win("h", 5, 7) == True # 用户选高,下一张牌更小→输 assert judge_win("h", 7, 5) == False # 用户选低,下一张牌更小→赢 assert judge_win("l", 7, 5) == True # 无效选择→输 assert judge_win("x", 5, 7) == False print("输赢判断测试通过!")
等这些小功能都没问题了,再用前面的模拟输入和固定随机数测试整个游戏流程:
def test_full_game(): # 模拟用户输入:下注10,选高,继续玩,下注10,选低,退出 test_inputs = ["10", "h", "y", "10", "l", "n"] # 固定随机种子,让牌的序列是5→7(赢)、6→3(赢) final_balance = high_low_game(initial_balance=100, inputs=test_inputs, card_seed=42) # 两次赢,每次加10,最终余额应该是120 assert final_balance == 120 print("完整游戏流程测试通过!")
给新手的小提醒
- 别一开始就测整个游戏:先拆成小模块,比如余额更新、输赢判断、输入验证,一个个测对,再拼起来,出问题也好定位。
- 用print辅助调试:测试的时候如果断言失败,多打印中间值,看看哪里和预期不一样。
- 不用追求完美:入门阶段能写出可测试的代码,能覆盖主要功能就够了,不用纠结复杂的测试框架。
最后附上修改后的highLowGame示例(整合了上面的方法),你可以直接参考着改自己的代码:
import random # 可模拟的输入函数 def get_user_input(prompt, test_inputs=None): if test_inputs is not None: return test_inputs.pop(0) return input(prompt) # 可替换的随机牌生成函数 def generate_card(seed=None): if seed is not None: random.seed(seed) return random.randint(1, 13) # 单独的余额更新函数 def update_balance(balance, bet, win): if win: return balance + bet elif win is None: # 平局 return balance else: return balance - bet # 单独的输赢判断函数 def judge_win(choice, current_card, next_card): if choice == 'h': return next_card > current_card elif choice == 'l': return next_card < current_card return False # 无效选择算输 # 主游戏函数,支持测试参数 def high_low_game(initial_balance=100, test_inputs=None, card_seed=None): balance = initial_balance if card_seed is not None: random.seed(card_seed) while balance > 0: print(f"\n当前余额: {balance}") # 获取下注金额 bet_str = get_user_input("请输入下注金额: ", test_inputs) try: bet = int(bet_str) except ValueError: print("请输入有效的数字!") continue if bet <= 0 or bet > balance: print("下注金额无效,请重新输入!") continue # 生成当前牌 current_card = generate_card() print(f"当前牌: {current_card}") # 获取用户选择 choice = get_user_input("高(H)还是低(L)? ", test_inputs).strip().lower() # 生成下一张牌 next_card = generate_card() print(f"下一张牌: {next_card}") # 判断结果并更新余额 if choice not in ['h', 'l']: print("无效选择,你输了!") balance = update_balance(balance, bet, win=False) else: win = judge_win(choice, current_card, next_card) if win: print("恭喜你赢了!") balance = update_balance(balance, bet, win=True) elif next_card == current_card: print("平局,退回下注金额!") balance = update_balance(balance, bet, win=None) else: print("很遗憾你输了!") balance = update_balance(balance, bet, win=False) # 询问是否继续 play_again = get_user_input("继续玩吗?(Y/N): ", test_inputs).strip().lower() if play_again != 'y': break print(f"\n游戏结束!最终余额: {balance}") return balance # 测试函数 def run_tests(): # 测试余额更新 assert update_balance(100, 20, True) == 120 assert update_balance(100, 20, False) == 80 assert update_balance(100, 20, None) == 100 print("✅ update_balance 测试通过") # 测试输赢判断 assert judge_win('h', 5, 7) == True assert judge_win('h', 7, 5) == False assert judge_win('l', 7, 5) == True assert judge_win('l', 5, 7) == False assert judge_win('x', 5, 7) == False print("✅ judge_win 测试通过") # 测试完整游戏(赢两次) test_inputs = ["10", "h", "y", "10", "l", "n"] final_balance = high_low_game(initial_balance=100, test_inputs=test_inputs, card_seed=42) assert final_balance == 120 print("✅ 完整游戏(赢两次)测试通过") # 测试完整游戏(输一次) test_inputs = ["10", "h", "n"] final_balance = high_low_game(initial_balance=100, test_inputs=test_inputs, card_seed=123) assert final_balance == 90 print("✅ 完整游戏(输一次)测试通过") print("\n🎉 所有测试都通过啦!") # 运行测试或启动游戏 if __name__ == "__main__": # 注释掉下面一行,换成high_low_game()就能正常玩游戏 run_tests() # high_low_game()
这样改完之后,你既能正常运行游戏让用户玩,又能通过run_tests()自动测试所有功能,完全符合教授的要求,而且都是入门级的写法,容易理解和修改。
内容的提问来源于stack exchange,提问作者Noah Blount

