On 5 Jun, 07:13 pm, [email protected] wrote:
Hi,

following up from ticket #6502, I'm looking for recommendations/best
practices for writing unit-tests for AMP-based code.

As described in the ticket, the issue I'm currently facing is that the
AMP implementation is subtly not re-entrant safe and doesn't work with a
synchronous transport, for example this code raises an exception:

http://twistedmatrix.com/trac/attachment/ticket/6502/example.py

I'm starting to think that the most appropriate testing strategy would
be to mock/stub AMP.callRemote (or the protocol class altogether)
instead of trying to use a fake transport.

Thoughts?

I think this is thinking in the right direction. Twisted generally tries to be responsible for testing its own code, and the serialization from commands to bytes (and the reverse) that AMP does is part of Twisted, so you should really be free from the burden of testing that that stuff works.

One thing it's worth noticing is that the AMP class itself contains very little code. Instead, it inherits most functionality from a few base classes. It's worth learning about the division of responsibility between these classes because they can be helpful in writing cleaner AMP code and - relevant to this topic - writing AMP unit tests.

One of the base classes, BinaryBoxProtocol, is almost entirely concerned with serialization logic. You can probably ignore this one entirely to begin with (although consider the consequences of serialization/deserialization living in this one class, independent of the rest of the protocol logic: perhaps you want to exchange AMP commands with a web browser and would find JSON an easier format to work with than AMP's int16 string scheme... etc).

Next, BoxDispatcher. This one is what actually implements `callRemote` and the reverse - ampBoxReceived, turning an AMP box (already parsed), into an incoming method call or the result of a previous outgoing method call. It operates on locator (to look up how to handle incoming method calls) and a box sender (to send out boxes representing method calls or responses). It doesn't know about the network, so your box sender can be a purely in-memory thing, implementing some box handling logic purely as Python code and no I/O.

As far as the locator goes, if you want the standard `@SomeCommand.responder` functionality, then you can easily get this by re-using the next base class of AMP, CommandLocator. This one's pretty straight-forward: subclass it and those decorators will work for you.

Ignore the last one, SimpleStringLocator, it's for extremely old-style AMP code that no one should be writing anymore.

So this all means that your application logic can all live on a CommandLocator subclass. When you really want to put this on an AMP server, you can hook an AMP instance up to your CommandLocator subclass (AMP takes a locator as an __init__ argument). When you want to test your command implementations, you can hook the CommandLocator up to a BoxDispatcher and a box sender and throw boxes straight at it with no network interation.

Some pieces are probably still missing from the public API - for example, you do want to test that your objects all get properly serialized and deserialized through AMP, particularly if you're implementing custom Argument types. There are some private APIs, _objectsToStrings and _stringsToObjects mostly, that really help with testing this, and we should think about how to expose this functionality publically. Also, we should document this whole pile of stuff. Maybe you'd be interested in writing something up after you've had a chance to play with these ideas?

Jean-Paul

_______________________________________________
Twisted-Python mailing list
[email protected]
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python

Reply via email to