我想做的是在命令行上运行
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 构建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
漂亮,干净,按书办事!
您在进入测试块之前执行代码,因此未达到预期。您需要在设置期望后运行测试块中的代码(例如,通过将
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
根据之前的答案/评论,使用新语法而不使用 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
您可以使用 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
与 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
对于任何遇到这个问题并且其他建议的解决方案都不起作用或干净/直接的人,特别是对于多行输出,这就是我解决我的问题的方法:
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"
。只需添加是不带引号的