単純な REPL の動作をテストするために RSpec を使用しています。REPL は、入力が「終了」でない限り、入力が何であれエコー バックします。その場合、ループは終了します。
テスト ランナーがハングしないようにするために、別のスレッド内で REPL メソッドを実行しています。sleep
スレッド内のコードが実行されたことを確認してから、それについて期待することを書くために、簡単な呼び出しを含める必要があることがわかりました。これを削除すると、スレッド内のコードが実行される前に予期が行われることがあるため、テストが断続的に失敗します。
sleep
ハックを必要とせずに、REPL の動作について決定論的に期待できるように、コードと仕様を構成する良い方法は何ですか?
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