如何使用rspec测试多线程TCPServer

时间:2014-12-08 22:40:28

标签: ruby multithreading rspec tcpserver

我写了一个非常简单的日期服务器:

require 'socket'

s = TCPServer.new 3939
  while (conn = s.accept)
    Thread.new(conn) do |c|
      c.print "Enter your name: "
      name = c.gets.chomp
      c.puts "Hi #{name}, the date is..."
      c.print `date`
      c.close
    end
  end

用户连接,生成一个线程,输入他们的名字,返回日期。简单。

我想知道如何在rspec中测试这样的东西。我有过的一些想法:1。)使用VCR记录服务器连接,使用Timecop冻结并返回日期。 2.)连接到前块中的实际服务器。我不完全确定如何执行此操作,因为当我运行rspec时,我认为它实际上运行服务器...或者发生了一些事情,终端只是冻结并等待某些东西......示例测试代码:

before
  @server = TCPSever.new 3939
end

it "does something.."
  conn = @server.accept
  # etc
end

after
  @server.close
end

3。)存根连接。 4.)甚至不要尝试测试创建的线程,只需将名称请求和日期响应放入方法并测试它。

我查看了puma的测试,看看他们如何测试线程: https://github.com/puma/puma/blob/master/test/test_puma_server.rb#L27

我真的很感激这方面的一些帮助。这只是一个简单的练习,我想看看如何最好地为这种服务器实现一些测试。比如,模拟用户连接并输入他们的名字。 提前谢谢。

1 个答案:

答案 0 :(得分:2)

我会考虑将服务器组织为具有此签名的类:

class DateServer
  # Initialize the server.  If port is 0, find
  # an unused port and use that.
  def initialize(port = 0)
  def start
  def stop
  def port    # return the bound port
end

然后你的测试设置并撕下服务器:

before do
  @server = Server.new
  @server.start
end

after do
  @server.stop
end

由于这个服务器没有副作用,你的测试将连接到它,发送一些东西,并得到一些东西:

it "prints the date" do
  @socket = TCPSocket.new("localhost", @server.port)
  @socket.puts "Fred"
  @socket.close_write
  output = @socket.read
  expect(output).to match /Hi Fred/
end

要检查输出是否包含日期,请使用正则表达式。这是一个未经测试且可能不正确的例子:

  expect(output).to match /\w+ \w+ +\d+ \d+:\d+:\d+ \w+ \d+/

或者使用例如timecop gem,以便测试可以强制时间,然后在日期检查完全匹配。

这个服务器没有副作用,但如果有,我会把副作用放在他们自己的对象中。让我们说当用户的名字是" Barney"时,服务器可以重新启动该框。不要让服务器直接执行此操作,而是让它调用" rebooter"宾语。在您的测试中,创建一个假的重启器并将其提供给服务器:

before do
  @rebooter = double(Rebooter)
  @server = Server.new(rebooter: @rebooter)
  @server.start
end

然后:

"it should reboot when Barney connects" do
  @rebooter.should_receive(:reboot)
  @socket = TCPSocket.new("localhost", @server.port)
  @socket.puts "Barney"
  @socket.close_write
  output = @socket.read
end