[TIP] mock library comparisons (was Re: Flexmock 0.7.3 released)

Michael Foord fuzzyman at voidspace.org.uk
Sun Feb 27 09:49:13 PST 2011


On 24/02/2011 22:10, Michael Foord wrote:
> On 24/02/2011 17:11, Herman Sheremetyev wrote:
>> On Fri, Feb 25, 2011 at 12:48 AM, Michael Foord
>> <fuzzyman at voidspace.org.uk>  wrote:
>>> [snip...]
>>> If anyone else writes a comparison I'll be very happy to criticise 
>>> (uh... I
>>> mean improve) their examples of using mock. :-)
>> I put up a few Mock examples. Comments welcome!
>>
>> http://has207.github.com/flexmock/compare.html
>>
>

Hey Herman,

I thought this was such a good idea I've added it to the mock 0.7.0 
documentation:

     http://www.voidspace.org.uk/python/mock/compare.html

(With credits to you and the Mox project for the original comparisons.)

Feel free to send me any updates. Authors or users of other mock 
libraries please also send in corrections / comparisons.

In this version the mock examples are all in their own "block". This 
*isn't* to highlight them specially, but its because I've made them all 
doctests so I can ensure they are kept up to date.

The source for this page is available in the mock repo, or online at:

     http://www.voidspace.org.uk/python/mock/_sources/compare.txt

All the best,

Michael Foord

> Pretty good. A few minor suggestions and filling in the missing ones 
> (all assuming mock 0.7.0):
>
> Simple Mock
> Make the last line: my_mock.some_method.assert_called_once_with()
>
> Creating Partial Mocks
> I *think* what you want (instead of using patch) is:
>
> mock = mock.Mock(spec=SomeObject)
>
> Ensure calls are made in a specific order:
>
> mock = mock.Mock(spec=SomeObject)
> #mock.method1.return_value = 'first thing' # entirely optional
> #mock.method2.return_value = 'second thing'
>
> mock.method1()
> mock.method2()
>
> assert mock.method_calls == [
>     ('method1',)
>     ('method2',)
> ]
>
> Raising exceptions (a minor change):
>
> my_mock.some_method.side_effect = SomeException("message")
>
>
> Override new instances of a class:
>
> with mock.patch('somemodule.Someclass') as MockClass:
>     MockClass.return_value = some_other_object
>     assert some_other_object == some_module.SomeClass()
>
> (alternatively use patch.object(somemodule, 'SomeClass') to patch on 
> the module as an object instead of by name)
>
> You can also use patch as a decorator instead of a context manager:
>
> @patch('somemodule.Someclass')
> def my_test_function(MockClass):
>     MockClass.return_value = some_other_object
>     assert some_other_object == some_module.SomeClass()
>
> Call the same method multiple times
>
> mock = Mock()
> mock.some_method()
> mock.some_method()
>
> assert mock.some_method.call_count >= 2
>
> (A lot of information about the calls is available, depending on 
> exactly what information you want. You don't need to do *anything* to 
> merely *allow* multiple calls though.)
>
>
> Mock chained methods
>
> my_mock = mock.Mock()
> # if you don't need 'some value' you can leave the default return 
> value in place
> # and assert on equality / identity rather than by value
> my_mock.method1.return_value.method2.return_value.method3.return_value 
> = 'some value'
>
> assert 'some_value' == my_mock.method1().method2().method3(arg1, arg2)
>
> method3 = my_mock.method1.return_value.method2.return_value.method3
> method3. assert_called_once_with(arg1, arg2)
>
> These look prettier if you keep around references to the intermediate 
> mocks rather than chaining it all on one line, but you can do it.
>
>
> How about adding an example of mocking a context manager:
>
> my_mock = mock.MagicMock()
>
> with my_mock:
>     pass
>
> my_mock.__enter__.assert_called_with()
> my_mock.__exit__.assert_called_with(None, None, None)
>
> Or even mocking out "open" when used as a context manager:
>
> First way:
>
> >>> my_mock = MagicMock()
> >>> with patch('__builtin__.open', my_mock):
> ...     manager = my_mock.return_value.__enter__.return_value
> ...     manager.read.return_value = 'some data'
> ...     with open('foo') as h:
> ...         data = h.read()
> ...
> >>> data
> 'some data'
> >>> my_mock.assert_called_once_with('foo')
>
>
> Second way:
>
> >>> with patch('__builtin__.open') as mock:
> ...     mock.return_value.__enter__ = lambda s: s
> ...     mock.return_value.__exit__ = Mock()
> ...     mock.return_value.read.return_value = 'some data'
> ...     with open('foo') as h:
> ...         data = h.read()
> ...
> >>> data
> 'some data'
> >>> mock.assert_called_once_with('foo')
>
>
>> Cheers,
>>
>> -Herman
>
>


-- 
http://www.voidspace.org.uk/

May you do good and not evil
May you find forgiveness for yourself and forgive others
May you share freely, never taking more than you give.
-- the sqlite blessing http://www.sqlite.org/different.html




More information about the testing-in-python mailing list