XpdWiki

FrontPage
RecentChanges
XtC
FindPage
PageIndex
XpApprentices

Set your name in
UserPreferences

Edit this page

Referenced by
XtC20020212
MockObjects
XtC20020115
PhilDawes
XtC20010904
MockMaker
XtC20020122
MockMaker.old




JSPWiki v2.0.52


MockObjectsPython


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

  • Be more disciplined in the refactoring and remember to change your mocks (but I don't like having to remember things)
  • Write more integration tests which test more thoroughly (which is kind of anti-mock I like my integration tests to be light, I don't like having more than the minimum number of tests fail when I have one error)
  • write a test to check that the mock and the real thing are kept in sync (but one of the nice things about mocking in python is that you only construct the mock in the test you need it for generally they are very lightweight)

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


Edit this page   More info...   Attach file...
This page last changed on 01-Aug-2005 08:53:22 BST by 82.35.6.137.