Python Programming, news on the Voidspace Python Projects and all things techie.
Simple mocking of open as a context manager
Using open as a context manager is a great way to ensure your file handles are closed properly and is becoming common:
with open('/some/path', 'w') as f:
f.write('something')
The issue is that even if you mock out the call to open it is the returned object that is used as a context manager (and has __enter__ and __exit__ called).
Using MagicMock from the mock library, we can mock out context managers very simply. However, mocking open is fiddly enough that a helper function is useful. Here mock_open creates and configures a MagicMock that behaves as a file context manager.
from mock import inPy3k, MagicMock
if inPy3k:
file_spec = ['_CHUNK_SIZE', '__enter__', '__eq__', '__exit__',
'__format__', '__ge__', '__gt__', '__hash__', '__iter__', '__le__',
'__lt__', '__ne__', '__next__', '__repr__', '__str__',
'_checkClosed', '_checkReadable', '_checkSeekable',
'_checkWritable', 'buffer', 'close', 'closed', 'detach',
'encoding', 'errors', 'fileno', 'flush', 'isatty',
'line_buffering', 'mode', 'name',
'newlines', 'peek', 'raw', 'read', 'read1', 'readable',
'readinto', 'readline', 'readlines', 'seek', 'seekable', 'tell',
'truncate', 'writable', 'write', 'writelines']
else:
file_spec = file
def mock_open(mock=None, data=None):
if mock is None:
mock = MagicMock(spec=file_spec)
handle = MagicMock(spec=file_spec)
handle.write.return_value = None
if data is None:
handle.__enter__.return_value = handle
else:
handle.__enter__.return_value = data
mock.return_value = handle
return mock
>>> m = mock_open()
>>> with patch('__main__.open', m, create=True):
... with open('foo', 'w') as h:
... h.write('some stuff')
...
>>> m.assert_called_once_with('foo', 'w')
>>> m.mock_calls
[call('foo', 'w'),
call().__enter__(),
call().write('some stuff'),
call().__exit__(None, None, None)]
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')
And for reading files, using a StringIO to represent the file handle:
>>> from StringIO import StringIO
>>> m = mock_open(data=StringIO('foo bar baz'))
>>> with patch('__main__.open', m, create=True):
... with open('foo') as h:
... result = h.read()
...
>>> m.assert_called_once_with('foo')
>>> assert result == 'foo bar baz'
Note that the StringIO will only be used for the data if open is used as a context manager. If you just configure and use mocks they will work whichever way open is used.
This helper function will be built into mock 0.9.
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2012-01-13 12:18:35 | |
Categories: Projects, Python, Hacking Tags: mock, recipe, testing
Mocks with some attributes not present
Mock objects, from the mock library, create attributes on demand. This allows them to pretend to be objects of any type.
What mock isn't so good at is pretending not to have attributes. You may want a mock object to return False to a hasattr call, or raise an AttributeError when an attribute is fetched. You can do this by providing an object as a spec for a mock, but that isn't always convenient.
Below is a subclass of Mock that allows you to "block" attributes by deleting them. Once deleted, accessing an attribute will raise an AttributeError.
from mock import Mock
deleted = object()
missing = object()
class DeletingMock(Mock):
def __delattr__(self, attr):
if attr in self.__dict__:
return super(DeletingMock, self).__delattr__(attr)
obj = self._mock_children.get(attr, missing)
if obj is deleted:
raise AttributeError(attr)
if obj is not missing:
del self._mock_children[attr]
self._mock_children[attr] = deleted
def __getattr__(self, attr):
result = super(DeletingMock, self).__getattr__(attr)
if result is deleted:
raise AttributeError(attr)
return result
>>> mock = DeletingMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
...
AttributeError: f
This functionality will probably be built into 0.9, or whichever version of mock comes after 0.8...
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2012-01-12 12:33:15 | |
Categories: Projects, Hacking, Python Tags: mock, testing, recipe
mock 0.8rc2: new release and development docs
I've pushed out a new release of mock. This fixes an inconsistency in the create_autospec api I discovered whilst working on the docs (yes I've really been working on the docs), and a fix for a bug with using ANY. (Thanks to Tom Davis for reporting this.)
You can download mock 0.8rc2 or install it with:
pip install -U mock==dev
mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects. The latest stable release is 0.7.2, which you can download from pypi.
There are two changes in 0.8rc2:
- Removed the configure keyword argument to create_autospec and allow arbitrary keyword arguments (for the Mock constructor) instead
- Fixed ANY equality with some types in assert_called_with calls
Unfortunately, if you were using the configure argument to create_autospec then you'll have to change code when you update to 0.8rc2, sorry. The good news is that there's now some documentation for the new features - including for create_autospec.
I've now switched mock to using one of the standard Sphinx themes, and the development documentation (updated automatically on every commit) is available at mock.readthedocs.org.
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2012-01-11 02:13:32 | |
Categories: Projects, Python Tags: mock, release, documentation
mock 0.8 release candidate 1 and handling mutable arguments
I've released mock 0.8 release candidate 1. You can download it it or install it with:
pip install -U mock==dev
mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects. The latest stable release is 0.7.2, which you can download from pypi.
The main improvement in 0.8 rc1 is performance improvements, particularly in constructing MagiMock. Instantiating MagicMock is substantially faster than it was in 0.7 already, however patch creates a MagicMock by default now (in 0.7 patch creates a Mock by default). Instantiating MagicMock in 0.8 is still slower than instantiating a Mock in 0.7, but the difference is much less than it used to be.
There are also a couple of bugfixes. The full changelog for 0.8 rc1 is:
- create_autospec on the return value of a mock class will use __call__ for the signature rather than __init__
- Performance improvement instantiating Mock and MagicMock
- Mocks used as magic methods have the same type as their parent instead of being hardcoded to MagicMock
The additional changes in 0.8 are explained in these blog entries:
- 0.8 beta 4 release
- 0.8 beta 3 release
- 0.8 beta 2 release
- 0.8 beta 1 release
- 0.8 alpha 2 release
- 0.8 alpha 1 release
Special thanks to Julian Berman for his help with diagnosing and improving performance in this release. I have started work on the doc changes and I'm hoping that the 0.8 final release won't be too far off.
Mock, and its now multifarious variants, are designed to "remember" arguments they are called with for making assertions about how they have been used. There are two ways it could do this: it could either keep references to the arguments or it could copy them.
Mock chooses to keep a reference to the arguments rather than copying them, mainly because this was easier to implement and met all my use cases at the time. As a consequence, if a mock is called with mutable arguments (like a set or a dictionary) that are changed (mutated) in between the call and the assertion then you have to make the assertion against the changed value rather than the value as it was at the call time. Here's an example of what I mean:
>>> from mock import MagicMock
>>> arg = set()
>>> m = MagicMock(return_value=None)
>>> m(arg)
>>> arg.add(1)
>>> m.assert_called_with(set())
Traceback (most recent call last):
...
AssertionError: Expected call: mock(set([]))
Actual call: mock(set([1]))
Even though m was called with an empty set, an assertion with an empty set fails.
The alternative would be to copy all arguments for every call (using copy.deepcopy). Unfortunately deepcopy is slow and fragile, and although it would solve this problem with mutable arguments it would break assertions that rely on object identity.
The mock documentation has an example showing two ways to solve this problem. There's a third way which is possibly more elegant, a subclass of MagicMock that deep-copies all arguments:
>>> from copy import deepcopy
>>> class CopyingMock(MagicMock):
... def __call__(self, *args, **kwargs):
... args = deepcopy(args)
... kwargs = deepcopy(kwargs)
... return super(CopyingMock, self).__call__(*args, **kwargs)
...
>>> c = CopyingMock(return_value=None)
>>> arg = set()
>>> c(arg)
>>> arg.add(1)
>>> c.assert_called_with(set())
>>> c.assert_called_with(arg)
Traceback (most recent call last):
...
AssertionError: Expected call: mock(set([1]))
Actual call: mock(set([]))
>>> c.foo
<CopyingMock name='mock.foo' id='...'>
When you subclass Mock or MagicMock all dynamically created attributes, and the return_value will use your subclass automatically. That means all children of a CopyingMock will also have the type CopyingMock.
This goes nicely with the new-in-0.8 new_callable argument to patch that allows you to specify the mock class to use:
with patch('some.object', new_callable=CopyMock) as mock_thing:
# do things...
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2011-12-29 13:04:47 | |
Categories: Python, Projects Tags: release, mock, testing, performance, copying, mutable
mock 0.8 beta 4 released: bugfix and minor features
I've released mock 0.8 beta 4. You can download it it or install it with:
pip install -U mock==dev
mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects. The latest stable release is 0.7.2, which you can download from pypi.
This is intended to be the last beta before final release. The only outstanding work before final release is extensive documentation reworking for the new features. Changes in beta 4 are:
- patch lookup is done at use time not at decoration time
- When attaching a Mock to another Mock as a magic method, calls are recorded in mock_calls
- Addition of attach_mock method
- Renamed the internal classes Sentinel and SentinelObject to prevent abuse
- BUGFIX: various issues around circular references with mocks (setting a mock return value to be itself etc)
The additional changes in 0.8 are explained in these blog entries:
The most important change is a bugfix. Due to a new feature in 0.8, mocks tracking calls to their return values in mock_calls, setting a mock as its own return value no longer worked. This and some other issues with circular references between mocks are now fixed.
There are a couple of other minor features.
The patch decorator, when you specify the object to be patched as a string, does the import to find the target when the patch is activated (the decorated method is called) instead of when the patch is created. This allows you to use patch with modules that don't exist when the test is imported but will exist when the test is executed (for example they are imported in a setUp or created dynamically by the system under test). It also means that if you specify the module incorrectly (a typo) you get a test failure rather than the whole test run being aborted.
There is a new method called attach_mock, this allows you to attach a mock to another mock so that calls to it will be tracked in method_calls and mock_calls. New in 0.8 you can do this anyway with freshly created mocks just by setting a mock as an attribute on another one. This doesn't work (calls aren't tracked) if the mock has a name. This limitation is to allow you to bypass the new feature, and to prevent it happening just because your test code sets a mock as an attribute on another mock (they may represent un-related objects).
Unfortunately, when patch creates a mock it gives it a name. This prevents you tracking calls to mocks created by patch by setting them as attributes on a "manager mock" (but is useful for debugging when you want to know where your mock comes from). You may want to do this where you want to track the order of calls to mocks created by patch. Here's an example of using the new attach_mock method with patch to assert not just that the expected calls are made, but that they are made in the expected order:
>>> import sys
>>> from mock import MagicMock, call, patch
>>> sys.modules['sqeee'] = MagicMock()
>>> with patch('sqeee.first') as first:
... with patch('sqeee.second') as second:
... manager = MagicMock(name='manager')
... manager.attach_mock(first, 'first')
... manager.attach_mock(second, 'second')
... first(1)
... second(2)
...
<MagicMock name='manager.first()' id='4300465424'>
<MagicMock name='manager.second()' id='4300494800'>
>>> assert manager.mock_calls == [call.first(1), call.second(2)]
Calls to the mocks created by the patches are tracked by the manager, so manager.mock_calls can be interrogated for asserts about how the mocks are used.
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2011-10-10 01:05:00 | |
Categories: Python, Projects Tags: mock, release, testing, beta
mock 0.8 beta 3 released: feature complete
I've released mock 0.8 beta 3. You can download it it or install it with:
pip install -U mock==dev
mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects. The latest stable release is 0.7.2, which you can download from pypi.
mock 0.8 beta 3 is now feature complete, and is hopefully the code that will be released as the final version. All that is left is documentation work, so now is the ideal time to try the beta!
The changes in beta 3 are:
- Improved repr for Mock.call_args and entries in Mock.call_args_list, Mock.method_calls and Mock.mock_calls
- Improved repr for mocks
- Mocks attached as attributes or return values to other mocks have calls recorded in method_calls and mock_calls of the parent (unless a name is already set on the child)
- Addition of mock_add_spec method for adding (or changing) a spec on an existing mock
- BUGFIX: minor fixes in the way mock_calls is worked out, especially for "intermediate" mocks in a call chain
The additional changes in 0.8 are explained in these blog entries:
The first two changes in 0.8 beta 3 are very unassuming, improved reprs. They're possibly one of the most useful changes in 0.8, particularly for introspecting a mock when it hasn't been used how you expected.
Firstly, mocks have a repr that tells you where they came from (even if they are the return value of another mock):
>>> m = MagicMock()
>>> m().foo().bar.baz()
<MagicMock name='mock().foo().bar.baz()' id='4300950800'>
Secondly, the call_args, call_args_list, method_calls and mock_calls all have much more friendly reprs:
>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3)
>>> m(a='fish')
>>> m.call_args
call(a='fish')
>>> m.call_args_list
[call(1, 2, 3), call(a='fish')]
>>> m = MagicMock()
>>> m.foo(), m.bar(1), m().baz(a='foo')
(<MagicMock name='mock.foo()' ...)
>>> m.mock_calls
[call.foo(), call.bar(1), call(), call().baz(a='foo')]
These reprs are coincidentally the same way you construct lists using the new call object for comparing with method_calls, call_args_list or mock_calls:
>>> expected_calls = [call.foo(), call.bar(1), call(), call().baz(a='foo')]
>>> m.mock_calls == expected_calls
True
The other two features in 0.8 beta 3 make it slightly easier to configure mocks. mock_add_spec allows you to add (or change) a spec on a mock. It even works with magic methods:
>>> class Foo(object):
... one = 1
...
>>> p = patch('__main__.Foo')
>>> m = p.start()
>>> m.one.mock_add_spec(int)
>>> int(m.one)
1
>>> list(m.one)
Traceback (most recent call last):
...
TypeError: 'MagicMock' object is not iterable
>>> m.one.mock_add_spec(list)
>>> list(m.one)
[]
>>> int(m.one)
Traceback (most recent call last):
...
TypeError: int() argument must be a string or a number, not 'MagicMock'
A more powerful way of creating mocks with specced attributes is autospec, but mock_add_spec can still be useful.
An alternative way of configuring a mock is to manually create mocks and set them as attributes. If you do this calls to these mocks will now be recorded in method_calls and mock_calls of the parent:
>>> m = MagicMock()
>>> m.one = MagicMock(spec=int)
>>> int(m.one)
1
>>> m.mock_calls
[call.one.__int__()]
(It also works for setting mocks as return values. Calls to the 'child mock' will be recorded in the mock_calls of the parent.)
Like this post? Digg it or Del.icio.us it.
mock 0.8 beta 2:bug fix and side_effect iterables
I've released mock 0.8 beta 2. You can download it it or install it with:
pip install -U mock==dev
mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects. The latest stable release is 0.7.2, which you can download from pypi.
There are only a few changes from the beta 1 release, the most important of these is a bugfix. 0.8 beta 1 turned call_args, and entries in call_args_list, into tuples with three members instead of two members as previously. This has now been fixed. In addition yhere is one api change and a couple of features added since the beta 1 release:
- Setting side_effect to an iterable will cause calls to the mock to return the next value from the iterable
- Added assert_any_call method
- Moved assert_has_calls from call lists onto mocks
- BUGFIX: call_args and all members of call_args_list are two tuples of (args, kwargs) again instead of three tuples of (name, args, kwargs)
For the full list of new features in the 0.8 release, read these blog entries:
The first of the new features in beta 2 simplifies creating mocks that return a sequence of values for a series of calls.
>>> from mock import Mock
>>> m = Mock(side_effect=[1, 2, 3])
>>> m(), m(), m()
(1, 2, 3)
There are also two new assert methods on mocks. The first of these, assert_any_call, checks that the mock has been called with particular arguments. Unlike assert_called_with it doesn't just check the most recent call, but will pass if any of the calls (in call_args_list) match the call you are checking:
>>> m = MagicMock(return_value=None)
>>> m(5, 6, 7, 8)
>>> m(1, 2, 3, 4)
>>> m.assert_any_call(5, 6, 7, 8)
>>> m.assert_any_call(a=3)
Traceback (most recent call last):
...
AssertionError: mock(a=3) call not found
The second new method is assert_has_calls. This method allows you to check multiple calls, and as it uses mock_calls you can make asserts for calls on child mocks. You provide a list of calls and assert_has_calls checks that those calls appear sequentially in the mock_calls_list but it doesn't fail if there are extra calls before or after the list of calls. If you switch on any_order then assert_has_calls just checks that all of the calls you provide in the list are present, but without worrying about order at all:
>>> m = MagicMock()
>>> m.foo()
>>> m.bar.baz(1, 2, 3)
>>> m.bing.bong()
>>> m.assert_has_calls([call.foo(), call.bar.baz(1, 2, 3)])
>>> kalls = [call.bing.bong(), call.foo()]
>>> m.assert_has_calls(kalls, any_order=True)
There are now only two new features being considered for 0.8 final (both minor and to do with the repr of callargs objects). They are issues 108 & 113.
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2011-08-04 10:48:24 | |
Categories: Projects, Python Tags: mock, testing, release, beta
mock 0.8 beta 1: easier asserts for multiple and chained calls
I've released mock 0.8 beta 1. You can download it it or install it with:
pip install -U mock==dev
mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects. The latest stable release is 0.7.2, which you can download from pypi.
Note
This release introduces a backwards incompatibility with .call_args and .call_args_list. .call_args, and all the entries in .call_args_list, are "three-tuples" of (name, args, kwargs) instead of "two-tuples" of (args, kwargs).
This means that indexing into .call_args will now fetch the wrong entry.
I will fix this in a new beta release.
This is a beta release, so mock 0.8 is now "feature complete". The only changes will be bugfixes or any revisions to the new features based on feedback from you. The beta 1 release contains all the new features from the 0.8 alpha 2 release, plus a few new ones.
The most important change in beta 1 is a new, and greatly improved, way of making assertions about multiple or chained calls to a mock. The new features in beta 1 are [1]:
- Addition of mock_calls list for all calls (including magic methods and chained calls)
- Extension of call object to support chained calls and callargs for better comparisons with or without names. call object has a call_list method for chained calls
- Mock call lists (call_args_list, method_calls & mock_calls) are now custom list objects that allow membership tests for "sub lists" and have an assert_has_calls method for unordered call checks
- patch.TEST_PREFIX for controlling how patchers recognise test methods when used to decorate a class
Please test the beta!
The current way of making assertions about multiple and chained calls is a bit clumsy. Mock have two lists that store how they are called, and how child mocks (methods) have been called. These are call_args_lists and method_calls. For chained calls you had to manually extract the return_value mocks and make assertions on them individually.
mock 0.8 includes two new features for making this simpler. The first is a new attribute, mock_calls that records all calls - to the mock object, its methods, magic methods and return value mocks. The second is a call object for simpler construction of expected calls:
>>> from mock import MagicMock, call
>>> mock = MagicMock()
>>> mock(1, 2, 3)
<mock.MagicMock object at 0x57c5b0>
>>> mock.first(a=3)
<mock.MagicMock object at 0x58e750>
>>> mock.second()
<mock.MagicMock object at 0x59adf0>
>>> int(mock)
1
>>> expected = [call(1, 2, 3), call.first(a=3), call.second(), call.__int__()]
>>> mock.mock_calls == expected
True
A chained call is really a series of calls, so mock_calls will have each individual call in it:
>>> mock = MagicMock()
>>> mock.foo(1).bar(spam='eggs').baz.foo_again(2)
<mock.MagicMock object at 0x5cf9d0>
>>> mock.mock_calls
[('foo', (1,), {}), ('foo().bar', (), {'spam': 'eggs'}),
('foo().bar().baz.foo_again', (2,), {})]
You can either just make an assertion about the last call, or call call_list() on the call object to generate the list for you:
>>> expected = call.foo(1).bar(spam='eggs').baz.foo_again(2)
>>> mock.mock_calls == expected.call_list()
True
Instead of being 'dumb lists' the attributes call_args_list, method_calls and mock_calls are now special call list objects (a subclass of list). They allow you to do a membership test (using in) for a list of calls, where you want to check that a sequence of calls is in the list but don't care if there are additional calls before or after it. Where you want to check that certain calls were made, but don't care about the order, you can use the assert_has_calls method:
>>> mock = MagicMock(return_value=None)
>>> mock(1, 2)
>>> mock(3, 4)
>>> mock(5, 6)
>>> mock(7, 8)
>>> [call(3, 4), call(5, 6)] in mock.mock_calls
True
>>> some_calls = [call(7, 8), call(3, 4), call(1, 2)]
>>> mock.mock_calls.assert_has_calls(some_calls)
When you decorate a class with any of the patchers (patch, patch.dict, patch.object and now patch.multiple) it decorates any method whose name begins with test. There is a little known feature of the unittest.TestLoader that you can change the testMethodPrefix attribute to use a different prefix for it to recognise test methods. If you use anything other than test then you can now change patch.TEST_PREFIX to match.
In 0.8 beta 1 is a bug fix to patch.multiple. patch.multiple can now create mock objects for you (multiple of them if you want) by providing arg=DEFAULT. Where patch.multiple is creating mocks for you it passes them into a decorated function by keyword (because keyword arguments to patch.multiple are unordered), when used as a context manager it returns a dictionary keyed by name:
>>> from mock import patch, DEFAULT
>>> import os
>>> with patch.multiple(os.path, isfile=DEFAULT, isdir=DEFAULT) as mocks:
... assert os.path.isfile is mocks['isfile']
... assert os.path.isdir is mocks['isdir']
...
The failure messages for assert_called_with and assert_called_once_with improved with this release. A minor feature, but nice change:
>>> m = MagicMock()
>>> m.something(1, 2, 3)
<mock.MagicMock object at 0x56fe50>
>>> m.something.assert_called_with(1, 2, 5)
Traceback (most recent call last):
...
AssertionError: Expected call: something(1, 2, 5)
Actual call: something(1, 2, 3)
The biggest thing to do before the 0.8 final release is to update the docs for the new features in 0.8. The full list of changes since 0.7.2 is quite long:
- Addition of mock_calls list for all calls (including magic methods and chained calls)
- Mock call lists (call_args_list, method_calls & mock_calls) are now custom list objects that allow membership tests for "sub lists" and have an assert_has_calls method for unordered call checks, plus a nicer representation if you str or print them
- patch and patch.object now create a MagicMock instead of a Mock by default
- The patchers (patch, patch.object and patch.dict), plus Mock and MagicMock, take arbitrary keyword arguments for configuration
- New mock method configure_mock for setting attributes and return values / side effects on the mock and its attributes
- Implemented auto-speccing (recursive, lazy speccing of mocks with mocked signatures for functions/methods), as the autospec argument to patch
- Added the create_autospec function for manually creating 'auto-specced' mocks
- patch.multiple for doing multiple patches in a single call, using keyword arguments
- New new_callable argument to patch and patch.object allowing you to pass in a class or callable object (instead of MagicMock) that will be called to replace the object being patched
- Addition of NonCallableMock and NonCallableMagicMock, mocks without a __call__ method
- Protocol methods on MagicMock are magic mocks, and are created lazily on first lookup. This means the result of calling a protocol method is a MagicMock instead of a Mock as it was previously
- Added ANY for ignoring arguments in assert_called_with calls
- Addition of call helper object
- In Python 2.6 or more recent, dir on a mock will report all the dynamically created attributes (or the full list of attributes if there is a spec) as well as all the mock methods and attributes.
- Module level FILTER_DIR added to control whether dir(mock) filters private attributes. True by default. Note that vars(Mock()) can still be used to get all instance attributes and dir(type(Mock()) will still return all the other attributes (irrespective of FILTER_DIR)
- patch.TEST_PREFIX for controlling how patchers recognise test methods when used to decorate a class
- Support for using Java exceptions as a side_effect on Jython
- Improved failure messages for assert_called_with and assert_called_once_with
- Added the Mock API (assert_called_with etc) to functions created by mocksignature
- Tuples as well as lists can be used to specify allowed methods for spec & spec_set arguments
- Calling stop on an unstarted patcher fails with a more meaningful error message
- BUGFIX: an error creating a patch, with nested patch decorators, won't leave patches in place
- BUGFIX: __truediv__ and __rtruediv__ not available as magic methods on mocks in Python 3
- BUGFIX: assert_called_with / assert_called_once_with can be used with self as a keyword argument
- BUGFIX: when patching a class with an explicit spec / spec_set (not a boolean) it applies "spec inheritance" to the return value of the created mock (the "instance")
- BUGFIX: remove the __unittest marker causing traceback truncation
- Removal of deprecated patch_object
- callargs changed to always be a three-tuple of (name, args, kwargs)
- Private attributes _name, _methods, '_children', _wraps and _parent (etc) renamed to reduce likelihood of clash with user attributes.
- Added license file to the distribution
In other mock news, the name clash in Fedora has finally been resolved and mock will soon be packaged as python-mock.
| [1] | As always, the full set of changes can be seen in the changelog in the repo. |
Like this post? Digg it or Del.icio.us it.
Mock subclasses and their attributes
This blog entry is about creating subclasses of mock.Mock. mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects. The latest stable release is 0.7.2, which you can download from pypi.
There are various reasons why you might want to subclass Mock. One reason might be to add helper methods. Here's a silly example:
>>> from mock import MagicMock
>>> class MyMock(MagicMock):
... def has_been_called(self):
... return self.called
...
>>> mymock = MyMock(return_value=None)
>>> mymock.has_been_called()
False
>>> mymock()
>>> mymock.has_been_called()
True
The standard behaviour for Mock instances is that attributes and the return value are of the same type as the mock they are accessed on. This is so that Mock attributes are Mocks and MagicMock attributes are MagicMocks [1]. So if you're subclassing to add helper methods then they'll also be available on the attributes and return value mock of instances of your subclass.
>>> mymock.foo.has_been_called()
False
>>> mymock.foo()
<mock.MyMock object at 0x5747b0>
>>> mymock.foo.has_been_called()
True
Sometimes this is inconvenient. For example, one user is subclassing mock to created a Twisted adaptor. Having this applied to attributes too actually causes errors.
Mock (in all its flavours) uses a method called _get_child_mock to create these "sub-mocks" for attributes and return values. You can prevent your subclass being used for attributes by overriding this method. The signature is that it takes arbitrary keyword arguments (**kwargs) which are then passed onto the mock constructor:
>>> class Subclass(MagicMock):
... def _get_child_mock(self, **kwargs):
... return MagicMock(**kwargs)
...
>>> mymock = Subclass()
>>> mymock.foo
<MagicMock name='mock.foo' id='5696720'>
>>> assert isinstance(mymock, Subclass)
>>> assert not isinstance(mymock.foo, Subclass)
>>> assert not isinstance(mymock(), Subclass)
This works with mock 0.7 and 0.8 (and possibly earlier versions but they're no longer supported officially), and in 0.8 will be documented and tested so you can rely on it continuing to work.
| [1] | Mock 0.8 introduces a couple of new mock types that aren't callable. Their attributes are callable (otherwise non-callable mocks couldn't have methods), so they're an exception to this rule. |
Like this post? Digg it or Del.icio.us it.
Posted by Fuzzyman on 2011-07-18 17:22:00 | |
Categories: Python, Projects, Hacking Tags: mock, sublassing
Mock 0.8 alpha 2: patch.multiple, new_callable and non-callable mocks
I've released mock 0.8 alpha 2. You can download it it or install it with:
pip install -U mock==dev
mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects. The latest stable release is 0.7.2, which you can download from pypi.
As this is an alpha release it isn't feature complete. There are still a lot of features I'm looking at. The alpha 2 release contains all the new features from the 0.8 alpha 1 release, plus the following major changes:
- patch.multiple for doing multiple patches in a single call, using keyword arguments
- New new_callable argument to patch and patch.object allowing you to pass in a class or callable object (instead of MagicMock) that will be called to replace the object being patched
- Addition of NonCallableMock and NonCallableMagicMock, mocks without a __call__ method
There are also the following more minor changes:
- Mocks created by patch have a MagicMock as the return_value where a class is being patched
- create_autospec can create non-callable mocks for non-callable objects. return_value mocks of classes will be non-callable unless the class has a __call__ method
- autospec creates a MagicMock without a spec for properties and slot descriptors, because we don't know the type of object they return
- Removed the "inherit" argument from create_autospec
- Calling stop on an unstarted patcher fails with a more meaningful error message
- BUGFIX: an error creating a patch, with nested patch decorators, won't leave patches in place
- BUGFIX: __truediv__ and __rtruediv__ not available as magic methods on mocks in Python 3
- BUGFIX: assert_called_with / assert_called_once_with can be used with self as a keyword argument
- BUGFIX: autospec for functions / methods with an argument named self that isn't the first argument no longer broken
- BUGFIX: when patching a class with an explicit spec / spec_set (not a boolean) it applies "spec inheritance" to the return value of the created mock (the "instance")
- BUGFIX: remove the __unittest marker causing traceback truncation
The most interesting new feature in 0.8 alpha 2 is the addition of patch.multiple. This allows you to make several patches to an object in a single call. The target object can either be passed in directly, or specified via a string to be imported. A typical usecase for this (in fact the usecase that inspired the feature) is doing multiple patches to django settings in one line:
with patch.multiple(settings, THING='thing', OTHER='other'):
...
with patch.multiple('django.conf.settings', THING='thing', OTHER='other'):
...
The new non-callable mocks are straightforward, and only occasionally useful. They're useful where code, like django templating, treats callable objects differently from non-callable ones.
callable_new exists partly to make it easier to use the different mock variants. For example to patch something with a non-callable mock:
@patch('thing.otherthing', new_callable=NonCallableMock)
def test_thing(self, mock_otherthing):
...
An alternative usecase is where you're re-using a decorated function or method and want to patch something with a new object each time. For example patching sys.stdout with a StringIO instance:
@patch('sys.stdout', new_callable=StringIO)
def helper_method(self, mock_stdout):
...
Like this post? Digg it or Del.icio.us it.
mock 0.8 alpha 1: New Features
This is a long entry, please forgive me. It describes all the new features in mock 0.8.0 alpha 1. The main reason I need to describe it here is that I haven't yet written the documentation.
mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects. The latest stable release is 0.7.2, which you can download from pypi.
You can try out the new features described below by downloading the new alpha release. Alternatively you can pip install it:
pip install -U mock==dev
As this is an alpha release it isn't feature complete. There are still a lot of features, mainly but not only small ones, I'm looking at. There are some exciting new features, so definitely worth trying out the alpha if any of them particularly interest you. None of these features are frozen, but the ones highlighted below are fully implemented and tested. I'd really like feedback on these changes, particularly if you dislike them or have comments.
Here's a summary of the important changes:
- new autospec functionality - lazy, recursive, automatic speccing (see below...)
- patchers create a MagicMock not a Mock
- patchers and Mock take arbitrary keyword arguments for setting attributes on mocks
- protocol methods on MagicMock are hooked up as MagicMocks not Mocks
- on Python 2.6+ dir(Mock()) has custom behaviour
- new helper objects: ANY and call
It's not obvious what all of these do just from the summary. Here's the full skinny, starting with the most important new feature "autospeccing".
Autospeccing
Autospeccing is based on the existing spec feature of mock. It limits the api of mocks to the api of an original object (the spec), but it is recursive (implemented lazily) so that attributes of mocks only have the same api as the attributes of the spec. In addition mocked functions / methods have the same call signature as the original so they raise a TypeError if they are called incorrectly. This feature is a better version of both the spec and mocksignature features in the current mock.
Before I explain how auto-speccing works, here's why it is needed.
Mock is a very powerful and flexible object, but it suffers from two flaws when used to mock out objects from a system under test. One of these flaws is specific to the Mock api and the other is a more general problem with using mock objects.
First the problem specific to Mock. Mock has two assert methods that are extremely handy: assert_called_with and assert_called_once_with.
>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assert_called_once_with(1, 2, 3)
>>> mock(1, 2, 3)
>>> mock.assert_called_once_with(1, 2, 3)
Traceback (most recent call last):
...
AssertionError: Expected to be called once. Called 2 times.
Because mocks auto-create attributes on demand, and allow you to call them with arbitrary arguments, if you misspell one of these assert methods then your assertion is gone:
>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assret_called_once_with(4, 5, 6)
Your tests can pass silently and incorrectly because of the typo.
The second issue is more general to mocking. If you refactor some of your code, rename members and so on, any tests for code that is still using the old api but uses mocks instead of the real objects will still pass. This means your tests can all pass even though your code is broken.
Note that this is another reason why you need integration tests as well as unit tests. Testing everything in isolation is all fine and dandy, but if you don't test how your units are "wired together" there is still lots of room for bugs that tests might have caught.
mock already provides a feature to help with this, called speccing. If you use a class or instance as the spec for a mock then you can only access attributes on the mock that exist on the real class:
>>> import urllib2
>>> mock = Mock(spec=urllib2.Request)
>>> mock.assret_called_with
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
The spec only applies to the mock itself, so we still have the same issue with any methods on the mock:
>>> mock.has_data()
<mock.Mock object at 0x...>
>>> mock.has_data.assret_called_with()
Auto-speccing solves this problem, and combines the features of mocksignature. You can either pass autospec=True to patch / patch.object or use the create_autospec function to create a mock with a spec. If you use the autospec=True argument to patch then the object that is being replaced will be used as the spec object. Because the speccing is done "lazily" (the spec is created as attributes on the mock are accessed) you can use it with very complex or deeply nested objects (like modules that import modules that import modules) without a big performance hit [1].
Here's an example of it in use:
>>> import urllib2
>>> patcher = patch('__main__.urllib2', autospec=True)
>>> mock_urllib2 = patcher.start()
>>> urllib2 is mock_urllib2
True
>>> urllib2.Request
<MagicMock name='urllib2.Request' spec='Request' id='...'>
You can see that urllib2.Request has a spec. urllib2.Request takes two arguments in the constructor, here's what happens if we try to call it incorrectly:
>>> req = urllib2.Request()
Traceback (most recent call last):
...
TypeError: <lambda>() takes at least 2 arguments (1 given)
The spec also applies to instantiated classes (i.e. the return value of specced mocks):
>>> req = urllib2.Request('foo', 'bar')
>>> req
<MagicMock spec='Request' id='8064976'>
Because the spec also applies to methods, any typos in our asserts will raise the correct error:
>>> req.add_header('spam', 'eggs')
<mock.MagicMock object at 0x...>
>>> req.add_header.assret_called_with
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
>>> req.add_header.assert_called_with('spam', 'eggs')
In many cases you will just be able to add autospec=True to your existing patch calls, but you will be protected against bugs due to typos and api changes.
As well as using autospec through patch there is a create_autospec that can be used to create autospecced mocks directly:
>>> from mock import create_autospec
>>> import urllib2
>>> mock_urllib2 = create_autospec(urllib2)
>>> mock_urllib2.Request('foo', 'bar')
<MagicMock spec='Request' id='...'>
This isn't without caveats and limitations however, which is why it has not been made the default behaviour. In order to know what attributes are available on the spec object, autospec has to introspect (access attributes) the spec. As you traverse attributes on the mock a corresponding traversal of the original object is happening under the hood. If any of your specced objects have properties or descriptors that can trigger code execution then you may not be able to use autospec. On the other hand it is much better to design your objects so that introspection is safe [2].
A more serious problem is that it is common for instance attributes to be created in the __init__ method and not to exist on the class at all. autospec can't know about any dynamically created attributes and restricts the api to visible attributes.
>>> class Something(object):
... def __init__(self):
... self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
There are a few different ways of resolving this problem. The easiest, but not necessarily the least annoying, way is to simply set the required attributes on the mock after creation. Just because autospec doesn't allow you to fetch attributes that don't exist on the spec it doesn't prevent you setting them:
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a = 33
...
There is a more aggressive version of both spec and autospec that does prevent you setting non-existent attributes. This is useful if you want to ensure your code only sets valid attributes too, but obviously it prevents this particular scenario:
>>> with patch('__main__.Something', autospec=True, spec_set=True):
... thing = Something()
... thing.a = 33
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
Probably the best way of solving the problem is to add class attributes as default values for instance members initialised in __init__. Note that if you are only setting default attributes in __init__ then providing them via class attributes (shared between instances of course) is faster too. e.g.
class Something(object):
a = 33
This brings up another issue. It is relatively common to provide a default value of None for members that will later be an object of a different type. None would be useless as a spec because it wouldn't let you access any attributes or methods on it. As None is never going to be useful as a spec, and probably indicates a member that will normally of some other type, autospec doesn't use a spec for members that are set to None. These will just be ordinary mocks (well - MagicMocks):
>>> class Something(object):
... member = None
...
>>> mock = create_autospec(Something)
>>> mock.member.foo.bar.baz()
<mock.MagicMock object at 0x...>
If modifying your production classes to add defaults isn't to your liking then there are more options. One of these is simply to use an instance as the spec rather than the class. The other is to create a subclass of the production class and add the defaults to the subclass without affecting the production class. Both of these require you to use an alternative object as the spec. Thankfully patch supports this - you can simply pass the alternative object as the autospec argument:
>>> class Something(object):
... def __init__(self):
... self.a = 33
...
>>> class SomethingForTest(Something):
... a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> mock.a
<MagicMock name='Something.a' spec='int' id='...'>
Changes to patchers
In 0.8.0 patch and patch.object return MagicMock instances instead of Mock instances by default. This is because MagicMock is more useful. You can still specify the object to be returned if you really need something patched to not be a MagicMock.
The patchers, plus Mock itself now take arbitrary keyword arguments to set attributes on the created mocks. This makes it easier to create and configure a mock in a single line. This was a several-times-requested feature.
>>> mock = Mock(foo=3, bar=6, baz=9)
>>> mock.foo, mock.bar, mock.baz
(3, 6, 9)
You can also set return_value and side_effect on mock attributes and access attributes of attributes. This uses the normal dotted syntax, but as this isn't valid for keyword arguments you'll have to pass it in as **'d dictionary.
>>> args = {'return_value': 3, 'foo.return_value': 6, 'bar.side_effect': KeyError, 'baz.spam': 'weeee'}
>>> mock = Mock(**args)
>>> mock()
3
>>> mock.foo()
6
>>> mock.bar()
Traceback (most recent call last):
...
KeyError
>>> mock.baz.spam
'weeee'
My initial thought was to always have the attributes passed in as a dictionary to another keyword argument. The advantage of this approach would have been that if you misspelled any keyword arguments to patch / Mock then you would get a sensible error message. Taking arbitrary keyword arguments means you set an attribute on the mock instead!
>>> mock = Mock(retrun_value=3)
>>> mock()
<mock.Mock object at 0x...>
>>> mock.retrun_value
3
Just being able to pass keyword arguments to create attributes is a much nicer api to use, out-weighing the downsides in my opinion. I'm open to feedback as to which is preferable.
patch.dict also takes keywords arguments to set entries in the dictionaries it is patching:
>>> with patch.dict('sys.modules', foo=Mock(name='mock_foo')):
... import foo
... print foo
...
<Mock name='mock_foo' id='...'>
As well as the patch and Mock constructors there is a new Mock method, configure_mock, that you can use to configure a mock after it has been created. It works in exactly the same way (through arbitrary keyword arguments).
dir and helper objects
call and ANY are available to import from mock as helper objects. They are basically unchanged from the recipes on the examples page of the documentation.
Mock has gained a custom __dir__ method. In Python 2.6, or any more recent version, dir(some_mock) shows only useful attributes and will include any dynamically created attributes that wouldn't normally be shown. If the mock was created with a spec (or autospec of course) then all the attributes from the original are shown, even if they haven't been accessed yet:
>>> dir(Mock())
['assert_called_once_with', 'assert_called_with', 'call_args', 'call_args_list',
'call_count', 'called', 'configure_mock', 'method_calls', 'mock_calls',
'reset_mock', 'return_value', 'side_effect']
>>> import urllib2
>>> dir(Mock(spec=urllib2))
['AbstractBasicAuthHandler', 'AbstractDigestAuthHandler', 'AbstractHTTPHandler',
'BaseHandler', 'CacheFTPHandler', 'FTPHandler', 'FileHandler', 'HTTPBasicAuthHandler',
'HTTPCookieProcessor', 'HTTPDefaultErrorHandler', 'HTTPDigestAuthHandler', 'HTTPError',
...]
By way of contrast, here's the behaviour with mock 0.7:
>>> dir(Mock())
['_Mock__get_return_value', '_Mock__return_value_doc', '_Mock__set_return_value', '__call__',
'__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattr__', '__getattribute__',
'__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_children',
...]
>>> import urllib2
>>> dir(Mock(spec=urllib2))
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__',
'__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '_children', '_methods', '_name', '_parent', '_return_value', '_spec_class',
'_spec_set', '_wraps', 'call_args', 'call_args_list', 'call_count', 'called', 'method_calls',
'side_effect']
As you can see, in 0.8 many of the not-very-useful (private to Mock rather than the thing being mocked) underscore and double underscore prefixed attributes have been filtered from the result of calling dir on a Mock. If you dislike this behaviour you can switch it off by setting the module level switch FILTER_DIR:
>>> import mock
>>> mock.FILTER_DIR = False
>>> dir(mock.Mock())
['_Mock__get_return_value', '_Mock__return_value_doc', '_Mock__set_return_value', '__call__',
'__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__format__', '__getattr__',
'__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__',
...]
Alternatively you can just use vars(my_mock) (instance members) and dir(type(my_mock)) (type members) to bypass the filtering irrespective of mock.FILTER_DIR.
It was implementing the custom dir behaviour that prompted the blog entry Implementing __dir__.
Protocol Methods on MagicMock
Another several-times-requested feature, but one that was much harder to implement than you would think. mock 0.7.0 introduces support for the Python protocol methods, along with the MagicMock which has most of the useful protocol methods hooked up for you.
In 0.7 the protocol methods are hooked up with Mocks, which mean that many of them return Mock instances when called. Several people requested that they be MagicMocks so that the result of calling a protocol method was a MagicMock. This is now done, by lazily creating the MagicMocks on first use.
You can now do the following (if you really want):
>>> m = MagicMock()
>>> m[1][2][3][4]
<mock.MagicMock object at 0x...>
That covers all the changes worth blogging about. Please try it out and let me know what you think (apart from the lack of documentation). There are a bunch of other minor changes. You can see them all in the changelog (in the repo).
| [1] | The initial implementation wasn't lazy. When you used autospec it would recursively mock out the object, including all attributes and attributes of attributes and so on. For very deeply nested objects this could take a long time and created tens of thousands of mocks! |
| [2] | This only applies to classes or already instantiated objects. Calling a mocked class to create a mock instance does not create a real instance. It is only attribute lookups - along with calls to dir - that are done. A way round this problem would have been to use getattr_static, which can fetch attributes without triggering code execution. Descriptors like classmethod and staticmethod need to be fetched correctly though, so that their signatures can be mocked correctly. |
Like this post? Digg it or Del.icio.us it.
Archives
Counter...

