我正在使用 RSpec 来测试一个简单的 REPL 的行为。REPL 只是回显输入的内容,除非输入是“退出”,在这种情况下它会终止循环。
为了避免挂起测试运行器,我在一个单独的线程中运行 REPL 方法。为了确保线程中的代码在我写关于它的期望之前已经执行,我发现有必要包含一个简短的sleep
调用。如果我删除它,测试会间歇性地失败,因为有时会在线程中的代码运行之前做出预期。
什么是构建代码和规范的好方法,这样我就可以确定地对 REPL 的行为做出预期,而无需sleep
hack?
这是 REPL 类和规范:
class REPL
def initialize(stdin = $stdin, stdout = $stdout)
@stdin = stdin
@stdout = stdout
end
def run
@stdout.puts "Type exit to end the session."
loop do
@stdout.print "$ "
input = @stdin.gets.to_s.chomp.strip
break if input == "exit"
@stdout.puts(input)
end
end
end
describe REPL do
let(:stdin) { StringIO.new }
let(:stdout) { StringIO.new }
let!(:thread) { Thread.new { subject.run } }
subject { described_class.new(stdin, stdout) }
# Removing this before hook causes the examples to fail intermittently
before { sleep 0.01 }
after { thread.kill if thread.alive? }
it "prints a message on how to end the session" do
expect(stdout.string).to match(/end the session/)
end
it "prints a prompt for user input" do
expect(stdout.string).to match(/\$ /)
end
it "echoes input" do
stdin.puts("foo")
stdin.rewind
expect(stdout.string).to match(/foo/)
end
end