The simplest possible mock library
It has a bitbucket repository which you can clone with hg clone https://email@example.com/jab/minimock/, download an archive of the tip from http://bitbucket.org/jab/minimock/get/tip.gz, or install from with easy_install MiniMock==dev. There is also a Google Group for the development mailing list which can be emailed at firstname.lastname@example.org.
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('email@example.com', 'firstname.lastname@example.org', ... 'Hi there!', 'How is it going?') Called smtplib.SMTP('localhost') Called smtp_connection.sendmail( 'email@example.com', ['firstname.lastname@example.org'], 'To: email@example.com\nFrom: firstname.lastname@example.org\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.
Mock objects have several attributes, all of which you can set when instantiating the object. To avoid name collision, all the attributes start with mock_, while the constructor arguments don’t.
- The name of the object, used when printing out messages. In the example about it was 'smtplib.SMTP'.
- When this object is called, it will return this value. By default it is None.
- Alternately, you can give an iterable of return results, like returns_iter=[1, 2, 3]; on each subsequent call it will return the next value.
- If given, this will be called to get the return value. In essence, this function will be the real implementation of the method.
- An exception (instance or class) that will be raised when this object is called.
- If this is true, everytime a new attribute is set on the mock object something will be printed. Otherwise attribute sets are silent, and only function calls print something.
So to create an object that always raises ValueError, do:
>>> dummy_module = Mock('mylibrary') >>> dummy_module.invalid_func.mock_raises = ValueError
Every attribute of a mock object will itself be another mock object, unless you specifically set it to something else. For instance, you can do:
>>> from minimock import Mock >>> dummy_module = Mock('mylibrary') >>> dummy_module.CONSTANT = 1
Then the CONSTANT value will persist. But you can also traverse to whatever object you want, and you will get another mock object.
Another technique for creating a mock object is the mock(...) function. This works like:
>>> from minimock import mock >>> import os.path >>> mock('os.path.isfile', returns=True)
This looks up the os.path.isfile object, and changes it to a mock object. Any keyword arguments you give (like returns=True in this example) will be used to create the mock object; you can also give a mock_obj keyword argument to pass in a mock object you’ve already created.
This function looks in the calling function to figure out what to replace (os.path.isfile in the example). You must import the proper modules first. Alternately you can pass in a dictionary like [locals(), globals()] for it to use for lookup.
To restore all the objects mocked with mock(), use minimock.restore() (with no arguments; all the mocks are kept track of).
- Deprecate MockTracker. TraceTracker should be used instead.
- Fix show_attrs=True bug (Kendrick Shaw)
- Explicitly passing tracker=None to the Mock constructor now suppresses tracking. If tracker is not passed it will still use Printer(sys.stdout) as before.
- Added MinimockOutputChecker which normalizes whitespace in function call traces; TraceTracker now uses this instead of doctest.OutputChecker (Ben Finney)
- Allow mocking of built-in functions.
- Added TraceTracker, a better Tracker to use with unittests (James Brady)
- Added MockTracker for use with unittests rather than doctests (James Brady)
- Fixed setting special attributes like mock_returns on already-created Mock objects (Toby White)
- Separated out printing to a class that accepts call information and provided an implementation that prints calls to a file.
- Added show_attrs
First official release.