codegourmet

savory code and other culinary highlights

Integration Tests With Celluloid::IO, Part 1

| Comments

While creating Celluloid based applications is more fun than you’d expect from multithreaded programming, testing them has some caveats.

Here’s how we did the test setup with MiniTest and as a bonus I’ll describe a hack to handle worker exceptions correctly in a second post

Testing a TCP Server

Let’s say we’re writing a server application that accepts data messages on a TCP port and writes them to a database. We’ll be using CelluloidIO for this.

Here’s a basic test helper which is pretty similar to the one proposed in the Celluloid Wiki:

test_helper.rb:

1
2
3
4
5
6
7
8
9
10
require 'celluloid/test'

Celluloid.boot

class MiniTest::Test
  def before_setup
    Celluloid.shutdown
    Celluloid.boot
  end
end

Our object under test is a TCPServer class that we want to boot up, send some messages to, verify it’s responses and then shut it down.

Our integration test could look something like this:

request_response_test.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class RequestResponseTest < MiniTest::Test

  def test_responds_correctly
    tcp_server = TCPServer.new
    tcp_server.start

    client = TCPClient.new
    client.send('hello_message')
    sleep 0.05
    response = client.read

    assert_equal 'hello_response', response
  end

end

Wrapping the Server and Client into helpers

While this looks straight forward, we don’t want to repeat it for every test. So let’s refactor:

request_response_test.rb:

1
2
3
4
5
6
7
8
9
10
11
class RequestResponseTest < MiniTest::Test

  def test_responds_correctly
    with_tcp_server do
      response = client_request_response('hello_message')
    end

    assert_equal 'hello_response', response
  end

end

test_helper.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
require 'celluloid/test'

Celluloid.boot

module CelluloidIOTest
  def before_setup
    Celluloid.shutdown
    Celluloid.boot
  end

  def with_tcp_server(&block)
    tcp_server = TCPServer.new
    tcp_server.start
    yield tcp_server
  end

  def client_request_response(message)
    client = TCPClient.new
    client.send('hello_message')
    sleep 0.05
    response = client.read
  end
end

class MiniTest::Test
  include CelluloidIOTest
end

Now we have a nice test setup where we can fire up a server for the duration of one test/block and can send messages similar to MiniTest::Rack get or post.

TODO exceptions: worker shutdown before exception collecting needed!

Happy Coding! – codegourmet

Comments