XpdWiki
Set your name in
UserPreferences Edit this page Referenced by
JSPWiki v2.0.52
![]() ![]() |
PhilDawes expressed some interest in MockObjects in Python at XtC. I remembered that DaveKirby posted a file download (PythonMock?.zip 3/29/01) to the yahoo group. It is available from: http://groups.yahoo.com/group/extremeprogramming/files/PythonMock.zip RachelD ... I've had a look at this today - it's cool! Like EasyMock? for Python (rather than MockMaker). You have to post analyze the calls that were made rather than set expectations then verify. IvanM I am writing a MockObjects package for Python that analyses calls at the time of invocation. Something like a port of MockObjectsRuby to Python, but a bit more fiddly because Python doesn't have closures. Feel free to contact me for more details, or if you want to collaborate. Or chat with me about it at an XtC evening. --NatPryce I now have a working version of a MockObject class that lets the test define expectations and a small library of expectation classes, including, most usefully, an expectation that wraps around any callable object to give it a human-friendly description for use in error messages. I'll demo it at the next XtC if anyone's interested. --NatPryce I am adding information to this page as I learn more about using MockObjects in Python. Please add any tips that you have... Default Arguments to Constructors Give a class constructor arguments with default values that create objects used by the classes real implementation. This allows tests to pass mock objects to the constructor, but by default the object initialises itself to work correctly. # A class that provides an interface to the system event queue class System_Event_Queue: def wait_for_event( self ): ... class Reactor: def __init__( self, event_queue = System_Event_Queue() ): self.event_queue = event_queue def run( self ): while 1: event = self.event_queue.wait_for_event() if event.type == EVENT_QUIT: return else: dispatch_event( event ) # Create a reactor that dispatches events from the system event queue reactor = Reactor() # Create a reactor that dispatches mocked events mock_queue = Mock_Event_Queue() reactor = Reactor( mock_queue ) Mock Modules Sometimes a class needs to use functions in a module that are defined at module scope, rather than as methods of a class. Module functions and object methods are both called in the same way, and a module is a Python object (albeit one without a class in Python 2.1), and so references to a module can be passed as parameters and stored in instance variables. E.g. Calling a module function: import module module.function() Calling an object method: object = Class() object.function() One can therefore define an MockObject that has the same methods as the functions exported from a module. References to the mock module can be used anywhere that references to the module are used (apart from import statements). One can therefore make a tested object take the module it uses as a constructor argument or method parameter and pass the mock module to the object when it is under test. Use default arguments to make the common case convenient. E.g. # In this example, the API to the system event queue is defined in terms # of the function 'wait_for_event' exported from module 'system_event_queue' # import system_event_queue class Reactor: def __init__( self, event_queue = system_event_queue ): self.event_queue = event_queue def run( self ): while 1: event = self.event_queue.wait_for_event() if event.type == EVENT_QUIT: return else: dispatch_event( event ) # Create a reactor that dispatches events from the system event queue module reactor = Reactor() # Create a reactor that dispatches events from a mocked event queue module mock_queue = Mock_Event_Queue_Module() reactor = Reactor( mock_queue ) Interfaces I think that mock testing in python (or any other dynamically typed language ) needs something to keep the interface of the mock in sync with the interface of the real object. What happened a couple of times was that I would refactor a class and have all the unit tests pass then the integration test would fail or worse it would fail when I actually ran the code for real. This was because classes which depended on the refactored class were tested using mocks which became out of sync with the real class. This alarmed me because it meant I couldn't just trust my unit tests. After discussion at xtc the possible solutions proposed were
Of these the latter appeals most because it should require least mental effort. The way I propose to do this is as follows: When testing class foobar and another class using MockFoobar? I will create a class in my unit test called FooBarInterface? like this: class FooBarInterface: def __init__(self,aFooBar): self.delegate=aFooBar def amethod(self,someargs): result=self.delegate.amethod(someargs) if not instanceof(result,thecorrectClass): raise "wrong return type for amethod" return result Then when I write tests for FooBar? I will wrap it in FooBarInterface? and when I create a MockFooBar? I will wrap it in FooBarInterface? as well. This should mean any changes to the FooBar? will be detected by tests using MockFooBar?. I will still need to be disciplined enough to actually remember to use the interface but this should be a one off task when I use my first Mock, which I think I can remember to do. This is the simplest, most verbose way of creating the interface and if i do use them a lot I'll try to come up with a general interface class that does not involve typing the method signature more than once. What I think is interesting about this is that it highlights the importance of the interface in mock testing which I had overlooked because you get it for free in Java. Also what I regard as the interface to a class can be as detailed or as vague as i like, I can decide which methods must be there and which I don't care about, which public attributes are there, I can assert things about the structure of the return value (but it's a bad thing that a client of the class should need to know about the structure of the return value) or whatever. Mind you I suspect that just asserting the return type is good enough. I also think it is better that this sort of interface checking is done in a unit test and not in the code or by static checking. This means I can decide exactly how much I want to test and not have a compiler tell me to change things I don't care about. --ChrisCottee PythonMock? now includes a mechanism for protecting against some forms of driftage between production class and mock-based tests. It will also soon have some support for mocking modules. See http://sourceforge.net/projects/python-mock/ see also http://xper.org/wiki/seminar/InterfacesInPython
|