首页 > 解决方案 > 如何使用 rspec 测试 .each 迭代块内的标准输出?

问题描述

我有一个示例代码:

  def print_stuff(first_num, second_num)
    puts 'hello'
    (first_num..second_num).to_a.each do |num|
      puts 'The current number is: '
      puts "#{num}"
    end
  end

我和使用 rspec,我想测试一下输出是否正确。我的尝试如下:

  expect(initialize_program(1, 3)).to output(
    "The current number is: 
    1
    The current number is: 
    2
    The current number is: 
    3").to_stdout

但是相反,我得到一个预期的块输出到标准输出,但不是块错误,因为 initialize_program(1,3) 正在输出文本,但它是在 .each 块内完成的,因此方法本身返回数字范围的数组.

如何测试块内的文本,以查看输出的文本是否正确?

谢谢!

标签: rubyunit-testingrspeciterationstdout

解决方案


重构以检查字符串,而不是标准输出

这种类型的代码是您应该首先编写测试的原因。您实际上是在测试 Kernel#puts,它总是返回nil,而不是验证您是否构建了您期望的字符串。不要那样做。相反,像这样重构:

def print_stuff(num1, num2)
  str =
    (num1..num2).map { |num|"The current number is: #{num}" }
    .join "\n"
  puts str
  str
end

print_stuff 1, 3
#=> "The current number is: 1\nThe current number is: 2\nThe current number is: 3"

这不仅会在标准输出上打印您期望的内容:

The current number is: 1
The current number is: 2
The current number is: 3

但也会使用方法最后一行的隐式返回来返回一个值,您可以使用该值与规范中的期望进行比较。

您还可以重构该方法以返回一个字符串对象数组,或者您可能明确想要测试的任何其他内容。您的真实方法越能反映您计划测试的内容越好。

RSpec 示例

RSpec.describe '#print_stuff' do
  it 'prints the expected message' do
    expected_string = <<~EOF
      The current number is: 1
      The current number is: 2
      The current number is: 3
    EOF
    expect(print_stuff 1, 3).to eql(expected_string.chomp)
  end

  # Even without the collection matchers removed in RSpec 3,
  # you can still validate the number of items returned.
  it 'returns the expected number of lines' do
    lines = print_stuff(1, 3).split("\n").count
    expect(lines).to eql(3)
  end
end

在 IRB 中测试 RSpec 示例

在 irb 中,您可以像这样验证您的规格:

require 'rspec'
include RSpec::Matchers

expected_string = <<~EOF
  The current number is: 1
  The current number is: 2
  The current number is: 3
EOF

# String#chomp is needed to strip the newline from the
# here-document
expect(print_stuff 1, 3).to eql(expected_string.chomp)

# test the returned object in other ways, if you want
lines = print_stuff(1, 3).split("\n").count
expect(lines).to eql(3)

推荐阅读