连接到后台 EventMachine 应用程序以进行单元测试
Connecting to background EventMachine application for unit testing
我正在编写一个无头 Ruby 应用程序,使用 EventMachine 通过套接字进行通信。我想为这个应用程序编写一些单元测试。这意味着我的 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 应用程序,并在它准备好后立即创建一个套接字连接?
不确定是否有更好的方法,但我使用 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
问题在于,从主线程您无法知道 Thread.new
代码块何时实际执行。通过使用 sleep
,您只需给它足够的时间,它就会被执行。
在这种情况下,我更喜欢使用 Queue
,其中 Thread.new
块在它完成后执行 push
(通常是 nil
)做它应该做的事情,而用于 sleep
的线程从中做 pop
。 pop
等到 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)
我正在编写一个无头 Ruby 应用程序,使用 EventMachine 通过套接字进行通信。我想为这个应用程序编写一些单元测试。这意味着我的 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 应用程序,并在它准备好后立即创建一个套接字连接?
不确定是否有更好的方法,但我使用 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
问题在于,从主线程您无法知道 Thread.new
代码块何时实际执行。通过使用 sleep
,您只需给它足够的时间,它就会被执行。
在这种情况下,我更喜欢使用 Queue
,其中 Thread.new
块在它完成后执行 push
(通常是 nil
)做它应该做的事情,而用于 sleep
的线程从中做 pop
。 pop
等到 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)