Python Programming, news on the Voidspace Python Projects and all things techie.
mock 0.8 beta 3 released: feature complete
pip install -U mock==dev
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.)
This work is licensed under a Creative Commons Attribution-Share Alike 2.0 License.