连接到后台EventMachine应用程序进行单元测试

时间:2015-08-01 04:29:24

标签: ruby macos sockets eventmachine

我正在编写一个使用EventMachine通过套接字进行通信的无头Ruby应用程序。我想为这个应用程序编写一些单元测试。这意味着我的Ruby测试脚本需要在后台启动该应用程序,与它进行套接字通信,然后关闭该过程。

此代码失败。套接字连接被拒绝。

require 'socket'
PORT = 7331
CMD = File.expand_path("../../bin/rb3jay",__FILE__)
@thread = Thread.new{ `#{CMD} -D --port #{PORT}` }
@socket = TCPSocket.open('localhost', PORT)
#=> Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 7331

如果我在尝试套接字连接之前注入2秒的延迟,它会按预期工作:

@thread = Thread.new{ `#{CMD} -D --port #{PORT}` }
sleep 2
@socket = TCPSocket.open('localhost', PORT)

这似乎是一个严重的黑客攻击。也许2秒对我的机器足够长,但在其他地方太短。

我应该如何在后台正确启动EventMachine应用程序,并在它准备就绪后立即创建套接字连接?

2 个答案:

答案 0 :(得分:2)

不确定是否有更好的方法,但我使用retry解决了这个问题:

@thread = Thread.new{ `#{CMD} -D --port #{PORT}` }
begin
  @socket = TCPSocket.open('localhost', PORT)
rescue Errno::ECONNREFUSED
  sleep 0.1
  retry
end

这将无限期地继续尝试每秒10次建立连接,直到它工作。更强大的解决方案可能是使用计数器或计时器最终放弃,以防出现严重错误。

完整的测试代码如下所示:

require 'socket'
require 'minitest/autorun'

PORT = 7331
CMD = File.expand_path("../../bin/rb3jay",__FILE__)

class TestServer < MiniTest::Unit::TestCase
  def setup
    @pid = Process.spawn "#{CMD} -D --port #{PORT}"
    begin
      @socket = TCPSocket.open('localhost', PORT)
    rescue Errno::ECONNREFUSED
      sleep 0.1
      retry
    end
  end

  def teardown
    if @socket && !@socket.closed?
      @socket.puts("quit") # try for a nice shutdown
      @socket.close
    end
    Process.kill("HUP",@pid)
  end

  def test_aaa
    # my test code
  end

  def test_bbb
    # more test code
  end
end

答案 1 :(得分:0)

问题在于,从主线程中您无法知道实际执行Thread.new代码块的时间。通过使用sleep,您只需给它足够的时间就可以执行它。

在这种情况下,我更喜欢使用Queue,其中Thread.new块在完成后执行了什么操作push(通常是nil)它应该这样做,而曾经sleep的线程从中做poppop会等到Queue

中有可用数据
require 'socket'
PORT = 7331
CMD = File.expand_path("../../bin/rb3jay",__FILE__)
q = Queue.new
@thread = Thread.new do
  Process.spawn "#{CMD} -D --port #{PORT}"
  q.push(nil)
end
q.pop
@socket = TCPSocket.open('localhost', PORT)

但是,您可能会遇到问题,因为生成命令并不意味着服务器实际上已准备就绪(侦听新连接)。所以,我尝试一种方法,因为我可以更好地控制服务器的生命周期。

require 'socket'
PORT = 7331
q = Queue.new
@thread = Thread.new do
  server = TCPServer.new PORT
  q.push(nil)

  loop do
    client = server.accept
    client.puts "Hello !"
    client.puts "Time is #{Time.now}"
    client.close
  end
end
q.pop
@socket = TCPSocket.open('localhost', PORT)