views:

80

answers:

3

I'm writing a Python client+server that uses gevent.socket for communication. SAre there any good ways of testing the socket-level operation of the code (for example, verifying that SSL connections with an invalid certificate will be rejected)? Or is it simplest to just spawn a real server?

Edit: I don't believe that "naive" mocking will be sufficient to test the SSL components because of the complex interactions involved. Am I wrong in that? Or is there a better way to test SSL'd stuff?

+4  A: 

There is another (IMO better) way: You should mock the library you are using. An example mocking helper for python is mox.

You don't need a set of servers with a valid certificate, another with an invalid certificate, with no ssl support at all, ones not responding to any packets at all, etc. You can simulate their behavior with a "dummy" client socket. The way it works with Mox is You first "teach" what it should expect and how it should react and then You execute Your real code on it while swapping the real gevent.socket for the mocked one. It requires some practice to get the hang of it, but it's worth it.

Reef
I don't think it's that simple… By design, SSL can't be replayed… So I'm not sure how I would go about mocking an SSL handshake.
David Wolever
I didn't say You can mock Your library with mox, I only said that You should mock it some way. In cases like that I sometimes hand-write a dummy class that is hard or impossible to mock with mox. Perhaps You should just mock gevent.ssl with mox? What exactly are You doing, are You implementing SSL handshake Yourself? If not, You should be able to mock it easily.
Reef
I need to let `gevent.ssl` run a handshake in the tests so that I can verify that it raises the errors I expect (and accepts the connections I think it should accept). You're right that, once the connection has been established, I can mock the interactions… But that doesn't quite cover what I want.
David Wolever
Then You are not unit-testing Your program! Your tagging misled me :) I think that You are trying to test how the library behaves because it's documentation doesn't really say what You need to know.
Reef
Ah, you are right. I have changed the offending tag.
David Wolever
Thanks. As for the real case - unless You like reading cPython's ssl module implementation, it's probably best to set up a server with ssl and test gevent.socket against it (at least You will be **sure** about how it works).
Reef
+2  A: 

Mocking and stubbing are great, but sometimes you need to take it up to the next level of integration. Since spawning a server, even a fakeish one, can take some time, consider a separate test suite (call them integration tests) might be in order.

"Test it like you are going to use it" is my guideline, and if you mock and stub so much that your test becomes trivial it's not that useful (though almost any test is better than none). If you are concerned about handling bad SSL certs, by all means make some bad ones and write a test fixture you can feed them to. If that means spawning a server, so be it. Maybe if that bugs you enough it will lead to a refactoring that will make it testable another way.

Bill Gribble
+2  A: 

You can easily start a server and then access it in a test case. The gevent's own test suite does exactly that for testing gevent's built-in servers.

For example:

class SimpleServer(gevent.server.StreamServer):

    def handle(self, socket, address):
        socket.sendall('hello and goodbye!')

class Test(unittest.TestCase):      

    def test(self):
        server = SimpleServer(('127.0.0.1', 0))
        server.start()
        client = gevent.socket.create_connection(('127.0.0.1', server.server_port))
        response = client.makefile().read()
        assert response == 'hello and goodbye!'
        server.stop()

Using 0 for the port value means the server will use any available port. After the server is started, the actual value chosen by bind is available as server_port attribute.

StreamServer supports SSL too, pass keyfile and certfile arguments to the constructor and it will wrap each socket with SSLObject before passing it to your handler.

If you don't use StreamServer and your server is based on Greenlet then indeed spawning it is what you should do. Don't forget to kill it at the end of the test case.

Starting a server and spawning a greenlet are fast operations in gevent, much faster than creating a new thread or process and you can easily create a new server for each test case. Just don't forget to cleanup as soon as you don't need the server anymore.

I believe there's no need to mock any of gevent API, it's much easier just to use it as servers and clients can happily live within the same process.

Denis Bilenko