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