Python Programming, news on the Voidspace Python Projects and all things techie.

unittest.mock and mock 1.0 alpha 1

emoticon:key One of the results of the Python Language Summit at PyCon 2012 is that mock is now in the Python standard library. In Python 3.3 mock is available as unittest.mock.

For those of you who may not know, mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects.

The standard distribution of mock that you all know and love will still be available as a backport of the standard library version, but the standard library is now where new mock development will take place. Inevitably this means development will slow down, with new feature releases following the Python release cycle. As the api has stabilised with the 0.8 release this is a good thing...

However, there were some changes I wanted to make (removing obsolete code and adding a few minor features) before moving into the standard library. This is now complete, and there is a shiny new 1.0 alpha 1 release that matches the version in the Python standard library.

You can install mock 1.0 alpha 1 with:

pip install -U mock==dev
easy_install mock

Changes since 0.8 are:

  • mocksignature, along with the mocksignature argument to patch, removed
  • Support for deleting attributes (accessing deleted attributes will raise an AttributeError)
  • Added the mock_open helper function for mocking the builtin open
  • __class__ is assignable, so a mock can pass an isinstance check without requiring a spec
  • Addition of PropertyMock, for mocking properties
  • MagicMocks made unorderable by default (in Python 3). The comparison methods (other than equality and inequality) now return NotImplemented
  • Propagate traceback info to support subclassing of _patch by other libraries
  • BUGFIX: passing multiple spec arguments to patchers (spec , spec_set and autospec) had unpredictable results, now it is an error
  • BUGFIX: using spec=True and create=True as arguments to patchers could result in using DEFAULT as the spec. Now it is an error instead
  • BUGFIX: using spec or autospec arguments to patchers, along with spec_set=True did not work correctly
  • BUGFIX: using an object that evaluates to False as a spec could be ignored
  • BUGFIX: a list as the spec argument to a patcher would always result in a non-callable mock. Now if __call__ is in the spec the mock is callable

Documentation for 1.0a1 can be found at mock.readthedocs.org.

This includes one backwards incompatible change - the removal of mocksignature in all its guises. Normally I would only remove features with a deprecation period, but I wanted it gone before moving mock in the standard library. mocksignature is completely replaced by the create_autospec function and the autospec argument to patch.

There are a whole series of bugfixes around the spec arguments to the patchers. I discovered these whilst taking a look at improving a couple of error messages. That was a rabbit hole that absorbed an entire Sunday afternoon!

There are still two issues I'd like to fix for Python 3.3, but no guarantees:

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2012-03-28 18:15:44 | |

Categories: , Tags: ,


mock 0.8 released

emoticon:envelope After more than six months development work mock 0.8 has been released. 0.8 is a big release with many new features, general improvements and bugfixes.

You can download mock 0.8.0 final from the PyPI page or install it with:

pip install -U mock

mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects.

The only changes in mock 0.8.0 final since 0.8rc2 are:

  • Improved repr of sentinel objects
  • ANY can be used for comparisons against call objects
  • The return value of MagicMock.__iter__ can be set to any iterable and isn't required to be an iterator

The full changelog since 0.7.2 is pretty large. Hopefully I'll do a series of blog posts explaining the major new features and how to use them. Several common patterns of testing with mock have become simpler in 0.8.

Several of the changes, particularly the improved reprs and error messages, come for free without you having to know anything about the other new features. For documentation on the new features, browse the links in the changelog.

Here's a brief example of the improved reprs / failure messages you get with mock 0.8:

>>> from mock import MagicMock
>>> m = MagicMock(name='foo')
>>> m.method(1, 2, 3).attribute['foo']
<MagicMock name='foo.method().attribute.__getitem__()' id='4300665616'>
>>> m.method.call_args
call(1, 2, 3)
>>> m.method.assert_called_with('some args')
Traceback (most recent call last):
  ...
AssertionError: Expected call: method('some args')
Actual call: method(1, 2, 3)

One of the best new features, for making assertions about several calls at once, is mock_calls in conjunction with the call object:

>>> from mock import MagicMock, call
>>> m = MagicMock(name='foo')
>>> config = {'method.return_value.chained.return_value.nested.return_value': 3}
>>> m.configure_mock(**config)
>>> m.method('arg').chained().nested('call')
3
>>> m.mock_calls
[call.method('arg'),
 call.method().chained(),
 call.method().chained().nested('call')]
>>> m.mock_calls == [call.method('arg'),
...  call.method().chained(),
...  call.method().chained().nested('call')]
True
>>> expected_calls = call.method('arg').chained().nested('call').call_list()
>>> m.mock_calls == expected_calls
True

If you're still using mock 0.7, and can't upgrade all your test code yet, the 0.7 documentation is available online here.

The full List of changes since 0.7:

mock 0.8.0 is the last version that will support Python 2.4.

  • Addition of mock_calls list for all calls (including magic methods and chained calls)
  • 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
  • New mock assert methods assert_any_call and assert_has_calls
  • 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
  • Setting side_effect to an iterable will cause calls to the mock to return the next value from the iterable
  • 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
  • Addition of mock_add_spec method for adding (or changing) a spec on an existing mock
  • 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
  • Addition of attach_mock method
  • Added ANY for ignoring arguments in assert_called_with calls
  • Addition of call helper object
  • Improved repr for mocks
  • Improved repr for call_args and entries in call_args_list, method_calls and mock_calls
  • Improved repr for sentinel objects
  • patch lookup is done at use time not at decoration time
  • 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.
  • 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
  • Mock call lists (call_args_list, method_calls & mock_calls) are now custom list objects that allow membership tests for "sub lists" and have a nicer representation if you str or print them
  • 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)
  • Improved failure messages for assert_called_with and assert_called_once_with
  • The return value of the MagicMock.__iter__ method can be set to any iterable and isn't required to be an iterator
  • 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
  • Renamed the internal classes Sentinel and SentinelObject to prevent abuse
  • 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
  • Private attributes _name, _methods, '_children', _wraps and _parent (etc) renamed to reduce likelihood of clash with user attributes.
  • Added license file to the distribution

Thanks to all those who suggested features, provided patches and helped test the mock 0.8 release. There are still plenty of ways mock can continue to improve, you can browse the issues list to get an idea of some of the outstanding feature requests and suggestions.

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2012-02-16 12:56:14 | |

Categories: , Tags: , ,


Simple mocking of open as a context manager

emoticon:globepage 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: , , Tags: , ,


Mocks with some attributes not present

emoticon:eyeballz 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: , , Tags: , ,


mock 0.8rc2: new release and development docs

emoticon:html 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: , Tags: , ,


mock 0.8 release candidate 1 and handling mutable arguments

emoticon:pencil 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:

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: , Tags: , , , , ,


mock 0.8 beta 4 released: bugfix and minor features

emoticon:men 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: , Tags: , , ,


mock 0.8 beta 3 released: feature complete

emoticon:halt 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.

Posted by Fuzzyman on 2011-08-17 23:35:36 | |

Categories: , Tags: , ,


mock 0.8 beta 2:bug fix and side_effect iterables

emoticon:target 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: , Tags: , , ,


mock 0.8 beta 1: easier asserts for multiple and chained calls

emoticon:globepage 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.

Posted by Fuzzyman on 2011-07-25 11:02:01 | |

Categories: , Tags: , ,


Mock subclasses and their attributes

emoticon:exclaim 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: , , Tags: ,


Hosted by Webfaction

Counter...