[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