Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask

Testing a Client-Server Application

by dreadpiratepeter (Priest)
on May 05, 2004 at 16:05 UTC ( #350829=perlquestion: print w/replies, xml ) Need Help??

dreadpiratepeter has asked for the wisdom of the Perl Monks concerning the following question:

I'm at a loss as to how to set up rigorous Test::More testing for an app I'm writing. The application consists of a server that runs a game played by multiple clients. There are always at least two clients involved and they alternately are waiting or acting based on messages from the server.

The issue is that I want to provide rigorous testing. In a perfect world I could just spawn a server, spawn the clients and run a scripted game, invoking tests as I go.

However I know that running tests from a child doesn't work. I looked at Test::MultiFork, but frankly the documentation is sparse and what there is makes my brain hurt.

I have a current inkling of a solution. I'm using Log::Log4Perl, I could setup a special logger that spits out state information, and have my test suite spawn the server and two scripted clients, let them interact in the background, and read the log output, running tests on expected vs actual behavior.
SERVER: started SERVER: accepting connections CLIENT1: send connect SERVER: accepted connection from client1 SERVER: send waiting for next connection to client1 CLIENT1: received waiting for next ocnnection CLIENT1: entering wait state CLIENT2: send connect
and so on. Then my test script can open a tail on the output and run tests like:
is(<INPUT>,"CLIENT1: entering wait state\n","client1 waiting");

Anyone have any comments or better ideas for how to test this beast?

"Worry is like a rocking chair. It gives you something to do, but it doesn't get you anywhere."

Edit by tye, change unclosed PRE tags to CODE

Replies are listed 'Best First'.
Re: Testing a Client-Server Application
by Steve_p (Priest) on May 05, 2004 at 16:20 UTC

    WWW::Mechanize has a module as part of the tests that implements a basic HTTP server. You could check out the test scripts there to see what its doing for some ideas.

Re: Testing a Client-Server Application
by paulbort (Hermit) on May 05, 2004 at 19:33 UTC
    Second the recommendation to look at WWW::Mechanize, if your clients are web-based. If not, it might make more sense to write three separate test scripts, (one server and two clients,) and run all three on the same machine, using a token to make the clients take turns (I get the impression that the server could just do what it has to based on what the clients do.)

    A way to do this would probably be with a couple of local sockets, where client 1 does its thing, then when it's ready to wait for client 2, it sends a message on the socket to client 2, and starts listening on the other socket for a message allowing it to resume. (touching files in a common directory works too, if you're used to that kind of semaphore.)

    On further consideration, properly written client test scripts will exercise much (most?) of the server code, and should be able to verify proper operation of the server to that extent. Then the server test script would only need to test things that clients don't exercise, and would not need to be running while the client tests are running. The testing clients would just run against a regular server.

    Spring: Forces, Coiled Again!
Re: Testing a Client-Server Application
by adrianh (Chancellor) on May 06, 2004 at 01:23 UTC
    However I know that running tests from a child doesn't work.

    What problems have you come across? What you need to do is:

    • disable test numbering
    • disable headers/endings in the child processes

    For example:

    use strict; use warnings; use Test::More 'no_plan'; my $Num_children = 3; my $Max_tests_per_child = 5; my $Builder = Test::More->builder; $Builder->use_numbers( 0 ); sub pass_and_pause { pass( "@_" ); sleep rand( 5 ); }; foreach my $child ( 1 .. $Num_children ) { my $pid = fork; die "fork failed ($!)\n" unless defined $pid; if ( $pid ) { diag( "forking child $child" ); } else { my $first_test = $Builder->current_test; $Builder->no_header; $Builder->no_ending; pass_and_pause( "child $child test $_" ) for ( 1 .. 1 + rand( $Max_tests_per_child ) ); exit $Builder->current_test - $first_test; }; }; until ( wait == -1 ) { my $num_tests_child_ran = $? >> 8; diag "child terminated after running $num_tests_child_ran tests"; $Builder->current_test( $Builder->current_test + $num_tests_child_ +ran ); };
Re: Testing a Client-Server Application
by etcshadow (Priest) on May 06, 2004 at 04:34 UTC
    I have had good luck with a record/playback model. That is:
    • log the traffic (at either a semantic level or a network level, depending on the particulars of the application), if possible, from a real scenario from your "control" code base (that is, either set up a monitor between the client and the server, or alter the client or server to embed a logging capability).
    • make a mock client which can replay the same traffic to the server, and track the output from the server, also vice-versa with a mock server (or only use a mock client if you only want to test the server, or vice-versa).
    • use the "control" logs obtained from the logger/monitor, and the mock client/server to play the same traffic back against the test codebase, and, likewise monitor it.
    • diff the control logs versus the test logs.
    A simple example would be to test a web server by taking your real web logs (possibly altered to include more information than would normally be included, like, for example, the POST string), and running them through a script which made an LWP request for each entry in the web log. Then, of course, diffing the HTML.

    Of course, the method is generally applicable to anything that allows you to spoof the client or the server (and I've used it for more than just web servers), but web servers make a nice example. A more general method would be simply to trap all the traffic on the socket and write it to a file (tracking what is server talking and what is client talking), and then play the one sides half of that conversation into the other side by, well, basically pasting it into a telnet window :-)

    This model is very helpful for situations where you want to make sure that no unexpected changes occured between your control and your test version. Obviously, you'll want to identify differences and decide whether or not they are expected differences. It's often useful to munge the diff (systematically remove approved differences), and/or to munge the input's to the diff (likewise, as a way to systematically remove differences... but actually by making the inputs more similar, rather than by clipping the output of the diff, which can often be more difficult).

    ------------ :Wq Not an editor command: Wq

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://350829]
Approved by Old_Gray_Bear
Front-paged by Tomte
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (2)
As of 2022-10-01 23:46 GMT
Find Nodes?
    Voting Booth?
    My preferred way to holiday/vacation is:

    Results (6 votes). Check out past polls.