[Eventmachine-talk] How to unit test EM-based server?

685 views
Skip to first unread message

Steve H

unread,
Jun 4, 2009, 6:47:21 PM6/4/09
to eventmac...@rubyforge.org
Hey folks,

I'm working on setting up some unit tests for this server I wrote, but
I'm having some difficulty wrapping my head around how this should
work with EM.

Typically you subclass your unit test from Test::Unit::TestCase,
right? So right now, I'm thinking something like this (to start):
require 'eventmachine'
require 'test/unit'
require 'assert2'

module UnitTestConnect
attr_accessor :name, :data_to_send, :response

def post_init
end

def connection_completed
send_data @data_to_send
end

def receive_data data
end

def unbind
end
end
class MyUnitTest < Test::Unit::TestCase
PORT = ARGV[0] || 9339
SERVER = ARGV[1] || '127.0.0.1'
def setup
EM::run { EM::connect(SERVER, PORT, UnitTestConnect) {|c| c.name =
"tester"; @conn = c } }
end

test_send_name
@data_to_send = "[name:#{@name}]"
# Somehow wait for response...
assert("named properly") { @response == "[named:#{@name}]"
end

def teardown
@@conn.close_connection
EM::stop_even_loop
end
end

Can/should I leverage EM::defer here somehow?

Thanks,
Steve
_______________________________________________
Eventmachine-talk mailing list
Eventmac...@rubyforge.org
http://rubyforge.org/mailman/listinfo/eventmachine-talk

Lourens Naudé

unread,
Jun 4, 2009, 7:00:29 PM6/4/09
to eventmac...@rubyforge.org
Steve H,

See http://github.com/tmm1/em-spec/tree/master

It ensures a single reactor loop for all test cases in a given context.

I have a miniunit version I use as well.Please do ping if you're
interested.

- Lourens

Roger Pack

unread,
Jun 4, 2009, 7:30:22 PM6/4/09
to eventmac...@rubyforge.org
> Typically you subclass your unit test from Test::Unit::TestCase,
> right?  So right now, I'm thinking something like this (to start):
> require 'eventmachine'
> require 'test/unit'
> require 'assert2'

check the tests in the test dir they might help you get on your way.
-=r

Daniel DeLeo

unread,
Jun 5, 2009, 10:57:59 AM6/5/09
to eventmac...@rubyforge.org
Steve,
I also have a fork of em-spec. What I did differently is that I
extracted the EventMachine wrapping into a single ``em'' method that
should work independently of rspec. The basics of how you might use it
are like this:

require "em-spec/rspec"

class YourTest <T::U::TC
include EM::SpecHelper

def test_something
em do
# your test code here
done
end
end
end

I have yet to try it with T::U, but there's no reason why it shouldn't
work. If you do give it a try, let me know how it turns out.

HTH,
Dan DeLeo

http://github.com/danielsdeleo/em-spec/tree/master

On Jun 4, 5:00 pm, Lourens Naudé <lour...@methodmissing.com> wrote:
> Steve H,
>
> Seehttp://github.com/tmm1/em-spec/tree/master

> > Eventmachine-t...@rubyforge.org


> >http://rubyforge.org/mailman/listinfo/eventmachine-talk
>
> _______________________________________________
> Eventmachine-talk mailing list

> Eventmachine-t...@rubyforge.orghttp://rubyforge.org/mailman/listinfo/eventmachine-talk

Steve Hull

unread,
Jun 5, 2009, 1:47:27 PM6/5/09
to eventmac...@rubyforge.org
OK sorry if I'm being a bit dense here -- I've never really written Rspec or Unit Tests before -- but could someone send me a simple example?

Suppose you wanted to test that the EchoServer would really return what you sent it.  What would your test look like?  A little sample code that's more complicated than testing Timers would be greatly appreciated...  :)

OR!!  Better yet -- suppose that you had a RequestCounter server (counts the number of  requests it's received since start up).  You want to test that counting works properly with a single client and with multiple clients.  How would that look (I'm especially curious as to how that would look with the rspec-style tests)?

So far, the only tests I really understand are the ones that come in the tests folder of the EventMachine gem.  But with those, there seems to be a simple pattern where, at most, one request is made, then in the "received_data" method of the client, the EM run loop is stopped.  So the only think I can think of is -- if I want to make, say, 5 calls, I'd have to keep a counter and stop the run loop after "received_data" is called 5 times.

-Steve

Aman Gupta

unread,
Jun 5, 2009, 2:07:26 PM6/5/09
to eventmac...@rubyforge.org

Aman Gupta

unread,
Jun 5, 2009, 3:18:20 PM6/5/09
to eventmac...@rubyforge.org
On Fri, Jun 5, 2009 at 7:57 AM, Daniel DeLeo <devnu...@gmail.com> wrote:
> Steve,
> I also have a fork of em-spec. What I did differently is that I
> extracted the EventMachine wrapping into a single ``em'' method that
> should work independently of rspec. The basics of how you might use it
> are like this:
>
> require "em-spec/rspec"
>
> class YourTest <T::U::TC
>  include EM::SpecHelper
>
>  def test_something
>    em do
>      # your test code here
>      done
>    end
>  end
> end

This is similar to the way the EM tests are written, and does not
require the use of fibers at all, since EM.run is a blocking call:

def test_something
EM.run do


# your test code here

EM.stop
end
end

The advantage of using fibers and em-spec is that there is only one EM
loop running for all your tests, instead of one loop that is started
and stopped per test. This allows you to maintain some state across
tests (which might be considered a flaw, depending on your testing
philosophies).

Aman

Steve Hull

unread,
Jun 5, 2009, 7:50:01 PM6/5/09
to eventmac...@rubyforge.org
Thanks for the example Aman....  I think you just blew my mind.  Not really because the example is so complex -- but really because it's so simple.  I was actually just writing a response about how your example doesn't help because I want to test the interaction of the server&client, when I realized that this is not true.  I *just* want to test the server.  So I deleted my response and here I am.

So, as you did here with the client, I can do something similar with the server.

I'll override send_data, and call receive_data in the tests, then assert that the sent_data is what I expect.

Awesome.

One more question: what exactly does the "EM.describe EventMachine do" call *do*?  It runs the event loop?  Something else??  I assume I should do "EM.describe MyServerModule do"... or is that incorrect?

Also: are we ensured order of the tests?  I obviously didn't send this quickly enough, so let me respond to something you said below:  using em-spec allows you to keep some state.  That would be a huge advantage to me, if I could ensure tests were executed in order.

Thanks!!  :)

-Steve

PS: I'll let you guys know how it goes.  There *are* some differences between EM servers and clients, so I won't exactly be able to duplicate your example and plug in my own classes, methods and tests.  Right?  

Aman Gupta

unread,
Jun 5, 2009, 8:58:54 PM6/5/09
to eventmac...@rubyforge.org
On Fri, Jun 5, 2009 at 4:50 PM, Steve Hull<p.w...@gmail.com> wrote:
> Thanks for the example Aman....  I think you just blew my mind.  Not really
> because the example is so complex -- but really because it's so simple.  I
> was actually just writing a response about how your example doesn't help
> because I want to test the interaction of the server&client, when I realized
> that this is not true.  I *just* want to test the server.  So I deleted my
> response and here I am.
> So, as you did here with the client, I can do something similar with the
> server.
> I'll override send_data, and call receive_data in the tests, then assert
> that the sent_data is what I expect.

Yep, that's the general idea and should work for both clients and
servers. If you're not testing any async code (i.e. passing in a block
to a function that is going to be invoked later), you don't need
fibers or em-spec. You can simply call send/receive_data and make sure
what you expect is happening.

If you do need to test callbacks, then fibers/em-spec are handy
because you can make your test 'wait' until the callback is called.

> Awesome.
> One more question: what exactly does the "EM.describe EventMachine do" call
> *do*?  It runs the event loop?  Something else??  I assume I should do
> "EM.describe MyServerModule do"... or is that incorrect?

Yes, you can pass in a string or a constant to describe based on what
you're testing behaviors for. EM.describe runs the event loop and
makes all test cases "pause" before moving onto the next test. You can
"resume" the test suite by calling done. This is handy because done
can be called from inside a callback that's called after some time.

describe 'timing' do
should 'wait 2 seconds' do
sleep 2
end
# won't get here until two seconds later
end

is emulated using

EM.describe 'timing' do
should 'wait 2 seconds' do
EM.add_timer(2){ done }
end
# won't get here until two seconds later
end

you can also do the same thing without fibers or em-spec (but you'll
have to start a reactor for each test):

describe 'timing' do
should 'wait 2 seconds' do
EM.run do
EM.add_timer(2){ EM.stop }
end
end
# won't get here until two seconds later
end

> Also: are we ensured order of the tests?  I obviously didn't send this
> quickly enough, so let me respond to something you said below:  using
> em-spec allows you to keep some state.  That would be a huge advantage to
> me, if I could ensure tests were executed in order.

This is precisely the advantage you get with em-spec. The tests are
run in order, and since they all share the same reactor instance, you
can test things in order.. i.e. test connecting to a server, then
sending it data, then receiving certain types of responses, etc.

Without em-spec, you'd have to start a new reactor and connect to the
server for each test.

> Thanks!!  :)
> -Steve
> PS: I'll let you guys know how it goes.  There *are* some differences
> between EM servers and clients, so I won't exactly be able to duplicate your
> example and plug in my own classes, methods and tests.  Right?

Yep, the basic ideas should remain the same. Good luck, let us know if
you have more questions.

Reply all
Reply to author
Forward
0 new messages