2

我正在使用 RSpec 来测试一个简单的 REPL 的行为。REPL 只是回显输入的内容,除非输入是“退出”,在这种情况下它会终止循环。

为了避免挂起测试运行器,我在一个单独的线程中运行 REPL 方法。为了确保线程中的代码在我写关于它的期望之前已经执行,我发现有必要包含一个简短的sleep调用。如果我删除它,测试会间歇性地失败,因为有时会在线程中的代码运行之前做出预期。

什么是构建代码和规范的好方法,这样我就可以确定地对 REPL 的行为做出预期,而无需sleephack?

这是 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
4

2 回答 2

1

您可以通过队列支持它,而不是让 :stdout 成为 StringIO。然后,当您尝试从队列中读取数据时,您的测试将一直等到 REPL 将某些内容推送到队列中(也就是写入标准输出)。

require 'thread'

class QueueIO
  def initialize
    @queue = Queue.new
  end

  def write(str)
    @queue.push(str)
  end

  def puts(str)
    write(str + "\n")
  end

  def read
    @queue.pop
  end
end

let(:stdout) { QueueIO.new }

我只是在没有尝试的情况下写了这个,它可能不足以满足您的需求,但它明白了这一点。如果你像这样使用数据结构来同步两个线程,那么你根本不需要休眠。由于这消除了不确定性,因此您不应该看到间歇性故障。

于 2013-04-30T02:51:13.530 回答
0

我在running?这种情况下使用了警卫。你可能无法完全避免睡眠,但你可以避免不必要的睡眠。

首先,running?向您的 REPL 类添加一个方法。

class REPL
  ...

  def running?
    !!@running
  end

  def run
    @running=true

    loop do
      ...
      if input == 'exit
        @running = false
        break
      end
      ...
    end
  end
end

然后,在您的规范中,休眠直到 REPL 运行:

describe REPL do
  ...
  before { sleep 0.01 until REPL.running? }
  ...
end
于 2013-04-29T04:42:36.577 回答