You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在RSpec测试中自动为gets方法提供输入以避免手动操作?

如何自动化测试依赖用户输入的Ruby类方法?

问题场景

我有一个处理用户输入的Ruby类方法,会循环请求输入直到获得非0正整数:

class Input
  # Method to get a valid number of days to assign prices to
  def Input.get_valid_number_of_days
    puts "Time to enter the number of days to be considered.\nUse positive integers only"
    # Loop requesting for a positive integer different than 0
    loop do
      print "How many days do you want to assign prices to? "
      days_number = gets.chomp
      # Readability improvement
      isnt_digit_only = /\D/.match?(days_number)
      return days_number.to_i if not isnt_digit_only and days_number != "0"
      # In case input is invalid
      puts "This is not a positive integer different from 0."
    end
  end
end

主程序通过number_of_days = Input.get_valid_number_of_days调用该方法。我尝试用RSpec自动化测试,但运行测试时出现错误:

Time to enter the number of days to be considered.
Use positive integers only
How many days do you want to assign prices to? 
An error occurred while loading ./spec/stock_picker_spec.rb.
Failure/Error: days_number = gets.chomp

Errno::ENOENT:
  No such file or directory @ rb_sysopen - --format
# ./lib/stock_picker.rb:12:in `gets'
# ./lib/stock_picker.rb:12:in `gets'
# ./lib/stock_picker.rb:12:in `block in get_valid_number_of_days'
# ./lib/stock_picker.rb:10:in `loop'
# ./lib/stock_picker.rb:10:in `get_valid_number_of_days'
# ./lib/stock_picker.rb:94:in `<top (required)>'
# ./spec/stock_picker_spec.rb:2:in `<top (required)>'
No examples found.

Finished in 0.00008 seconds (files took 0.19796 seconds to load)
0 examples, 0 failures, 1 error occurred outside of examples

我的测试代码如下:

require "./lib/stock_picker"

# Test input related methods
describe Input do
  describe ".get_valid_number_of_days" do
    it "Return a positive integer different than 0 when valid input" do
      allow($stdin).to receive(:gets).and_return("3\n")
      days = $stdin.gets
      expect(days).to be_kind_of(Integer)
    end
  end
end

解决方案

1. 修复文件加载时的自动执行问题

报错核心原因:你的lib/stock_picker.rb在加载阶段(第94行)直接调用了Input.get_valid_number_of_days,导致RSpec启动时就触发了交互逻辑。此时测试用例的Mock还未生效,gets会读取RSpec的命令行参数(如--format),从而抛出错误。

解决方法:将主程序逻辑封装到入口方法中,仅当直接运行脚本时才执行:

# lib/stock_picker.rb
class Input
  # ... 你的类方法代码 ...
end

# 封装主程序逻辑
def run_program
  number_of_days = Input.get_valid_number_of_days
  # 其他业务逻辑
end

# 只有直接运行该文件时才执行主程序
if __FILE__ == $0
  run_program
end

2. 修正RSpec测试逻辑

你的测试用例没有调用目标方法,只是单独读取了$stdin,完全没覆盖到Input.get_valid_number_of_days的逻辑。修正后的测试代码:

require "./lib/stock_picker"

describe Input do
  describe ".get_valid_number_of_days" do
    it "返回非0正整数当输入有效时" do
      # Mock $stdin的gets方法,返回模拟输入
      allow($stdin).to receive(:gets).and_return("3\n")
      # 调用要测试的类方法
      result = Input.get_valid_number_of_days
      # 断言结果符合要求
      expect(result).to be_a(Integer)
      expect(result).to eq(3)
      expect(result).to be > 0
    end

    it "拒绝非数字输入并重新请求" do
      # 先返回无效输入,再返回有效输入
      allow($stdin).to receive(:gets).and_return("abc\n", "2\n")
      # 验证错误提示是否输出
      expect { Input.get_valid_number_of_days }.to output(
        /This is not a positive integer different from 0./
      ).to_stdout
      result = Input.get_valid_number_of_days
      expect(result).to eq(2)
    end

    it "拒绝0输入并重新请求" do
      allow($stdin).to receive(:gets).and_return("0\n", "5\n")
      expect { Input.get_valid_number_of_days }.to output(
        /This is not a positive integer different from 0./
      ).to_stdout
      result = Input.get_valid_number_of_days
      expect(result).to eq(5)
    end
  end
end

3. 可选:隔离输入输出依赖(更优实践)

为了让代码更易测试,避免依赖全局变量$stdin和$stdout,可以将它们作为参数传入方法:

class Input
  def self.get_valid_number_of_days(input: $stdin, output: $stdout)
    output.puts "Time to enter the number of days to be considered.\nUse positive integers only"
    loop do
      output.print "How many days do you want to assign prices to? "
      days_number = input.gets.chomp
      isnt_digit_only = /\D/.match?(days_number)
      return days_number.to_i unless isnt_digit_only || days_number == "0"
      output.puts "This is not a positive integer different from 0."
    end
  end
end

测试时用StringIO模拟输入输出,无需Mock全局变量:

require "stringio"
require "./lib/stock_picker"

describe Input do
  describe ".get_valid_number_of_days" do
    it "处理有效输入并返回正确结果" do
      input = StringIO.new("3\n")
      output = StringIO.new
      result = Input.get_valid_number_of_days(input: input, output: output)
      
      expect(result).to eq(3)
      expect(output.string).to include("How many days do you want to assign prices to? ")
    end

    it "处理无效输入并输出提示" do
      input = StringIO.new("abc\n0\n4\n")
      output = StringIO.new
      result = Input.get_valid_number_of_days(input: input, output: output)
      
      expect(result).to eq(4)
      expect(output.string).to include("This is not a positive integer different from 0.")
    end
  end
end

内容的提问来源于stack exchange,提问作者Neblinus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 14:25:56