[Chicago] minimock

Ian Bicking ianb at colorstudy.com
Fri Nov 10 18:35:43 CET 2006


Hi guys.  I thought about doing a lightning talk last night, but it was 
getting kind of late at that point... anyway, kind of an off-the-cuff 
implementation of a mock library for use with doctest 
(http://python.org/doc/current/lib/module-doctest.html).

I wrote it up in a couple minutes here: 
http://svn.colorstudy.com/home/ianb/recipes/minimock.py
Also copied below:


r"""
minimock is a simple library for doing Mock objects with doctest.
When using doctest, mock objects can be very simple.

Here's an example of something we might test, a simple email sender::

     >>> import smtplib
     >>> def send_email(from_addr, to_addr, subject, body):
     ...     conn = smtplib.SMTP('localhost')
     ...     msg = 'To: %s\nFrom: %s\nSubject: %s\n\n%s' % (
     ...         to_addr, from_addr, subject, body)
     ...     conn.sendmail(from_addr, [to_addr], msg)
     ...     conn.quit()

Now we want to make a mock ``smtplib.SMTP`` object.  We'll have to
inject our mock into the ``smtplib`` module::

     >>> smtplib.SMTP = Mock('smtplib.SMTP')
     >>> smtplib.SMTP.mock_returns = Mock('smtp_connection')

Now we do the test::

     >>> send_email('ianb at colorstudy.com', 'joe at example.com',
     ...            'Hi there!', 'How is it going?')
     Called smtplib.SMTP('localhost')
     Called smtp_connection.sendmail(
         'ianb at colorstudy.com',
         ['joe at example.com'],
         'To: joe at example.com\nFrom: ianb at colorstudy.com\nSubject: Hi 
there!\n\nHow is it going?')
     Called smtp_connection.quit()

Voila!  We've tested implicitly that no unexpected methods were called
on the object.  We've also tested the arguments that the mock object
got.  We've provided fake return calls (for the ``smtplib.SMTP()``
constructor).  These are all the core parts of a mock library.  The
implementation is simple because most of the work is done by doctest.
"""

class Mock(object):

     def __init__(self, name):
         self.name = name
         self.mock_returns = None
         self.mock_attrs = {}

     def __repr__(self):
         return '<Mock %s %s>' % (hex(id(self)), self.name)

     def __call__(self, *args, **kw):
         parts = [repr(a) for a in args]
         parts.extend(
             '%s=%r' % (items) for items in sorted(kw.items()))
         msg = 'Called %s(%s)' % (self.name, ', '.join(parts))
         if len(msg) > 80:
             msg = 'Called %s(\n    %s)' % (
                 self.name, ',\n    '.join(parts))
         print msg
         return self.mock_returns

     def __getattr__(self, attr):
         if self.name:
             new_name = self.name + '.' + attr
         else:
             new_name = attr
         mock = Mock(new_name)
         self.mock_attrs[attr] = mock
         return mock


if __name__ == '__main__':
     import doctest
     doctest.testmod()



More information about the Chicago mailing list