Testing a Protocol Class

I’m in the process of writing a series of classes to connect to an AOL
OSCAR server (the servers used by AIM), but I’m not quite sure how to
set up tests for the classes. Basically, what I have now is a test.rb
file that creates a new thread to run a mock OSCAR server. The test
code then connects to the mock server and does its thing. I then
check the log messages to see if the authentication and such have gone
as they should.

I’d like some way to use Test::Unit to run unit tests for this
project, but I’m not entirely sure how to do that, since I can’t test
individual methods unless there are sockets set up for transfer. Is
my mock server approach appropriate or is there some other way to do
unit testing for a protocol client?

Bill Atkins

Hi,

I’m in the process of writing a series of classes to connect to an AOL
OSCAR server (the servers used by AIM), but I’m not quite sure how to
set up tests for the classes. Basically, what I have now is a test.rb
file that creates a new thread to run a mock OSCAR server. The test
code then connects to the mock server and does its thing. I then
check the log messages to see if the authentication and such have gone
as they should.

I’d like some way to use Test::Unit to run unit tests for this
project, but I’m not entirely sure how to do that, since I can’t test
individual methods unless there are sockets set up for transfer. Is
my mock server approach appropriate or is there some other way to do
unit testing for a protocol client?

I test my own network code similarly. The closest situation
I have to yours is probably my IRC client.

I do have one situation, testing higher level code, where I
run a mock IRC server in a separate thread. But that’s when
I’m testing “application” code that’s using my IRC client
class. For testing the IRC client itself, I (effectively)
run the client on a separate thread, and mock the server
with direct print() and gets() from the test functions.

Essentially:

SERVER_HOST = “localhost”
SERVER_PORT = 12345

def test_irccomm
  # This is creating the "mock IRC server" on localhost
  server = TCPServer.new(SERVER_PORT)

  # Here I'm starting up the IRC client handler that I
  # want to test, pointing it at my "mock server"...

  # Note that my IRC client does its i/o in a background
  # thread, which is why I said I "effectively" run
  # the client on a separate thread. . . . If the client
  # did blocking i/o in the foreground, then presumably
  # we'd create a separate thread here in which to place
  # the client.  Because it's the mock server that I 
  # want to be in the "foreground" here...
  cbh = TestIRCCallbackHandler.new
  irc = IRCComm.new(SERVER_HOST, SERVER_PORT, cbh)

  # Tell the client to initiate a connection.
  # Note that the connect method doesn't wait for
  # a result, it just makes the socket connection to
  # the server, and issues the appropriate first
  # registration handshake.
  assert( irc.eof )
  irc.connect(NICK1, USER, FULLNAME, USER_HOST)
  assert( ! irc.eof )

  # Now our "mock server" will accept the connection...
  sv_client = server.accept
  # ...and respond with some initial boilerplate:
  send_connect_notice(sv_client)

  # Now the mock server verifies the client sent the 
  # correct initial connection info:
  assert_equal( "NICK #{NICK1}\r\n", sv_client.gets )
  assert_equal( "USER #{USER} #{USER_HOST} #{SERVER_HOST} :#{FULLNAME}\r\n", sv_client.gets )

  # The mock server responds with a problem:    
  sv_client.print ":#{SERVER_HOST} 433 * #{NICK1} :Nickname is already in use.\r\n"

  # This is a hack for testing purposes... Added a waitio
  # method in the client to force it to deal with the
  # server response, before we continue...
  irc.waitio

  # The mock server verifies the client has responded
  # with a "second choice" nickname...
  assert_equal( "NICK #{NICK2}\r\n", sv_client.gets )

  # . . . etc . . .

end

Anyway… hope this helps. I have several networking classes
I test with Test::Unit and they all seem to work out something
like the above, with the “mock server” in the foreground doing
the print() gets() assert() calls, and the client effectively
in the background.

Regards,

Bill

···

From: “Bill Atkins” dejaspam@batkins.com

Hello Bill,

I would suggest to use a separate class that abstracts from the
transportation layer (TCP) and has a functional interface that
corresponds 1:1 with the protocol commands.

Testing the transportation layer is another test case and should
include all the possible failure problems on a network.

Mixing both testcases into a huge one makes testing much harder, and
takes more lines of code to implement.

···


Best regards,
Lothar mailto:mailinglists@scriptolutions.com