Ruby MiniTest测试含循环的用户输入代码时遇无限循环,如何解决?
使用MiniTest测试带循环的用户输入验证代码
问题场景
要测试一段Ruby用户输入合法性验证代码,被测代码如下:
def player_input(min, max) loop do user_input = gets.chomp verified_number = verify_input(min, max, user_input.to_i) if user_input.match?(/^\d+$/) return verified_number if verified_number puts "Input error! Please enter a number between #{min} or #{max}." end end def verify_input(min, max, input) return input if input.between?(min, max) end
自行编写的测试代码通过stub :gets模拟无效输入,断言错误提示,但测试陷入无限循环:
def test_when_user_inputs_an_incorrect_value_runs_loop_once invalid_input = '11' @game_input.stub(:gets, invalid_input) do min = @game_input.instance_variable_get(:@minimum) max = @game_input.instance_variable_get(:@maximum) error_message = "Input error! Please enter a number between #{min} or #{max}." assert_output(error_message, "") { @game_input.player_input(min, max) } end end
试过将输出改为STDERR.puts断言标准错误,问题仍未解决,询问如何测试这类带循环的用户输入代码。
解决方案
1. 让Stub的gets返回多轮输入,终止循环
之前的测试无限循环,是因为stub的gets始终返回同一个无效输入,player_input的loop永远无法拿到通过验证的输入。可以让stub的返回值是一个lambda,依次返回无效输入+有效输入,这样循环执行一次错误提示后,就能拿到有效输入退出:
def test_invalid_input_triggers_error_then_exits_with_valid_input min = 1 max = 10 # 先返回无效输入,再返回有效输入 test_inputs = ['11', '5'] @game_input.stub(:gets, -> { test_inputs.shift }) do # puts会自动添加换行符,所以错误消息要包含\n error_msg = "Input error! Please enter a number between #{min} or #{max}.\n" # 同时断言输出和方法返回值 assert_output(error_msg) { assert_equal 5, @game_input.player_input(min, max) } end end
2. 拆分测试关注点,降低复杂度
- 单独测试
verify_input方法:直接传入不同边界值,验证逻辑正确性,无需涉及输入循环:def test_verify_input assert_equal 5, verify_input(1, 10, 5) assert_nil verify_input(1, 10, 0) assert_nil verify_input(1, 10, 11) end - 测试
player_input的循环逻辑:重点验证无效输入触发错误提示、有效输入正常返回这两个核心场景,通过多轮stub输入覆盖流程即可。
3. 注意assert_output的换行符问题
puts方法会自动在输出末尾添加换行符,所以断言的错误消息必须包含\n,否则assert_output会因为字符串不匹配而失败。
内容的提问来源于stack exchange,提问作者user17370916
相关产品推荐
相关产品推荐

