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

Herman Sheremetyev herman at swebpage.com
Sun Feb 27 10:08:12 PST 2011


Ah cool, good timing :) I just finished adding your suggestions to my
own version. I haven't gotten around to doing the context manager bit,
but I should be able to whip up at least the flexmock and mox versions
for that later this week.

I was also thinking we should maybe start a thread here and get the
authors or enthusiasts of the other mock libraries to submit their own
versions for the stuff that I was missing.

Cheers,

-Herman

On Mon, Feb 28, 2011 at 2:49 AM, Michael Foord
<fuzzyman at voidspace.org.uk> wrote:
> 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