How to test a multi-threaded TCPServer with rspec

坚强是说给别人听的谎言 提交于 2020-01-04 06:10:51

问题


I've written a really simple date server:

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

A user connects, a thread is spawned, they enter their name, the date is returned. Simple.

I'm wondering about how I would go abouts testing something like this in rspec. Some ideas that I've had: 1.) Use VCR to record a server connection and Timecop to freeze and return a date. 2.) Connect to the actual server in a before block. I'm not entirely sure how to do this as when I run rspec, I think it actually runs the server...or something happens that the terminal just kind of freezes and waits for something... example test code:

before
  @server = TCPSever.new 3939
end

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

after
  @server.close
end

3.) Stub the connection. 4.) Don't even try to test that threads are created, and just put the name request and date response into a method and test this.

I looked at puma's tests to see how they test threads: https://github.com/puma/puma/blob/master/test/test_puma_server.rb#L27

I'd really appreciate some help with this. It's just a simple exercise where I'd like to see how best to implement some tests for this kind of server. Like, simulating a user connecting and entering their name. Thank you in advance.


回答1:


I would consider organizing the server as a class with this signature:

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

Your test then sets up and tears down the server:

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

after do
  @server.stop
end

Since this server has no side-effects, your test will connect to it, send some stuff, and get some stuff back:

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

To check that the output contains the date, either use a regular expression. Here's an untested and probably incorrect example:

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

Or use, e.g., the timecop gem so that the test can force the time, and then check for an exact match on the date.

This server has no side-effects, but if it did, I would put the side-effects in their own objects. Let's say that the server can reboot the box when the user's name is "Barney". Instead of having the server do that directly, let it call a "rebooter" object. In your test, create a fake rebooter and give that to the server:

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

and then:

"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


来源:https://stackoverflow.com/questions/27368345/how-to-test-a-multi-threaded-tcpserver-with-rspec

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!