[Python-Dev] Socket servers in the test suite

Vinay Sajip vinay_sajip at yahoo.co.uk
Mon May 2 16:26:56 CEST 2011


Nick Coghlan <ncoghlan <at> gmail.com> writes:

> sure the urllib tests already fire up a local server). Starting down
> the path of standardisation of that test functionality would be good.

I've made a start with test_logging.py by implementing some potential server
classes for use in tests: in the latest test_logging.py, the servers are between
comments containing the text "server_helper".

The basic approach for implementing socket servers is traditionally to use a
request handler class which implements the custom logic, but for some testing
applications this is overkill - you just want to be able to pass a handling
callable which is, say, a test case method. So the signatures of the servers are
all like this:

__init__(self, listen_addr, handler, poll_interval ...)

Initialise using the specified listen address and handler callable. Internally,
a RequestHandler subclass will be used whose handle() delegates to the handler
callable passed in.
A zero port number can be passed in, and a port attribute will (after binding)
have the actual port number used, so that clients can connect on that port.

start()

Start the server on a separate thread, using the poll_interval specified in the
underlying poll()/select() call. Before this is called, the request handler
class could be replaced with a subclass if need be.

stop(timeout=None)

Ask the server to stop and wait for the server thread to terminate.

The server also has a ready attribute which is a threading.Event, set just when
the server is entering its service loop. Typical mode of use would be:

class ClientTestCase(unittest.TestCase):
    def setUp(self):
        self.server = TheAppropriateServerClass(('localhost', 0),
self.handle_request, 0.01, ...)
        self.server.start()
        self.server.ready.wait()
        self.handled = threading.Event()

    def tearDown(self):
        self.server.stop(1.0) # wait up to 1 sec for thread to stop

    def handle_request(self, request):
        # Handle the request, e.g. by setting some attributes based on what
        # was received at the server
        # Set the flag to say we finished handling
        self.handled.set()

    def test_xxx(self):
        # set up client and send stuff to server
        # Wait for server to finish doing stuff
        self.handled.wait()
        # make assertions based on the attributes
        # set during request handling

The server classes provided are TestSMTPServer, TestTCPServer, TestUDPServer and
TestHTTPServer. There are examples of actual usage in test_logging.py:
SMTPHandlerTest, SocketHandlerTest, DatagramHandlerTest, SysLogHandlerTest,
HTTPHandlerTest.

I'd like some comments on this suggested API. I have not yet looked at how to
adapt other stdlib code than test_logging to use these classes, but the above
usage mode seems convenient and sufficient for testing applications. No doubt
people will be able to suggest problems with/improvements to the approach
outlined above.

Regards,

Vinay Sajip



More information about the Python-Dev mailing list