如何测试rspec中的puts

问题描述 投票:0回答:6

我想做的是在命令行上运行

ruby sayhello.rb
,然后接收
Hello from Rspec

我已经明白了:

class Hello
  def speak
    puts 'Hello from RSpec'
  end
end

hi = Hello.new #brings my object into existence
hi.speak

现在我想在 rspec 中编写一个测试来检查命令行输出实际上是“Hello from RSpec” 而不是“我喜欢 Unix”

不工作。目前我的 sayhello_spec.rb 文件中有这个

require_relative 'sayhello.rb' #points to file so I can 'see' it

describe "sayhello.rb" do
  it "should say 'Hello from Rspec' when ran" do        
    STDOUT.should_receive(:puts).with('Hello from RSpec')    
  end
end

另外,我需要实际看看测试在我的 RSPEC 中应该是什么样子。

rspec
6个回答
43
投票

我认为最好的方法是在输出匹配器中使用 rspec 构建https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher

例如, 这是你的课

class MakeIt
  def awesome(text)
    puts "Awesome #{text}"
  end
end

和你的测试

describe MakeIt do
  describe '#awesome' do
    it 'prints awesome things' do
      expect do
        MakeIt.new.awesome('tests')
      end.to output('Awesome tests').to_stdout
    end

    it 'does not print not awesome things' do
      expect do
        MakeIt.new.awesome('tests')
      end.to_not output('Not awesome tests').to_stdout
    end
  end
end

漂亮,干净,按书办事!


17
投票

您在进入测试块之前执行代码,因此未达到预期。您需要在设置期望后运行测试块中的代码(例如,通过将

require_relative
语句移到
STDOUT....
语句之后),如下所示:

describe "sayhello.rb" do
  it "should say 'Hello from Rspec' when ran" do        
    STDOUT.should_receive(:puts).with('Hello from RSpec')
    require_relative 'sayhello.rb' #load/run the file 
  end
end

16
投票

根据之前的答案/评论,使用新语法而不使用 gem 的解决方案如下所示:

describe "sayhello.rb" do
  it "should say 'Hello from Rspec' when run" do        
    expect(STDOUT).to receive(:puts).with('Hello from RSpec')
    require_relative 'sayhello.rb'  # load/run the file 
  end
end

12
投票

您可以使用 Rails 的 active_support 库来解决此问题,该库添加了

capture
方法:

require 'active_support/core_ext/kernel/reporting'
require_relative 'sayhello'

describe Hello do
  it "says 'Hello from RSpec' when ran" do
    output = capture(:stdout) do
      hi = Hello.new
      hi.speak
    end
    expect(output).to include 'Hello from RSpec'
  end
end

4
投票

与 bswinnerton 的答案有些相似,可以捕获

puts
输出,然后针对捕获的输出进行测试,而不必使用依赖于库的
capture
方法(有人提到该方法在 Rails 5 中已被弃用)。

Ruby 有一个名为

$stdout
的全局变量,默认情况下由常量
STDOUT
填充。
STDOUT
是将数据发送到 ruby 进程的标准输出流(不确定“流”是否是正确的术语)。 基本上在天真的情况下
STDOUT.puts("foo")
将导致“foo “出现在终端窗口中。
$stdout.puts("foo")
会做同样的事情,因为
$stdout
变量名称指的是
STDOUT
除非你重新分配它(这里的关键点)。最后
puts("foo")
$stdout.puts("foo")
的语法糖

接下来的策略是将

$stdout
重新分配给本地
IO
实例,您可以在运行代码后检查该实例,以查看其内容中是否出现“Hello from RSpec”。

这将如何运作:

describe "sayhello.rb" do
  it "should say 'Hello from Rspec' when ran" do        
    $stdout = StringIO.new

    # run the code
    # (a little funky; would prefer Hello.new.speak here but only changing one thing at a time)
    require_relative 'sayhello.rb' 

    $stdout.rewind   # IOs act like a tape so we gotta rewind before we play it back  

    expect($stdout.gets.strip).to eq('Hello from Rspec')
  end
end

0
投票

对于任何遇到这个问题并且其他建议的解决方案都不起作用或干净/直接的人,特别是对于多行输出,这就是我解决我的问题的方法:

subject { require_relative 'sayhello.rb' }

it "prints to stdout" do
  expect { subject }.to output(
    <<~OUTPUT
      Hello from Rspec
      Another line of thing that prints
      Another line with variable #{variable}
    OUTPUT
  ).to_stdout
end

并不是说您不需要包含

"
喜欢
"Hello from Rspec"
。只需添加是不带引号的

© www.soinside.com 2019 - 2024. All rights reserved.