python/mock-1.0.0/docs/compare.txt

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 =========================
     2  Mock Library Comparison
     3 =========================
     6 .. testsetup::
     8     def assertEqual(a, b):
     9         assert a == b, ("%r != %r" % (a, b))
    11     def assertRaises(Exc, func):
    12         try:
    13             func()
    14         except Exc:
    15             return
    16         assert False, ("%s not raised" % Exc)
    18     sys.modules['somemodule'] = somemodule = mock.Mock(name='somemodule')
    19     class SomeException(Exception):
    20         some_method = method1 = method2 = None
    21     some_other_object = SomeObject = SomeException
    24 A side-by-side comparison of how to accomplish some basic tasks with mock and
    25 some other popular Python mocking libraries and frameworks.
    27 These are:
    29 * `flexmock <http://pypi.python.org/pypi/flexmock>`_
    30 * `mox <http://pypi.python.org/pypi/mox>`_
    31 * `Mocker <http://niemeyer.net/mocker>`_
    32 * `dingus <http://pypi.python.org/pypi/dingus>`_
    33 * `fudge <http://pypi.python.org/pypi/fudge>`_
    35 Popular python mocking frameworks not yet represented here include
    36 `MiniMock <http://pypi.python.org/pypi/MiniMock>`_.
    38 `pMock <http://pmock.sourceforge.net/>`_ (last release 2004 and doesn't import
    39 in recent versions of Python) and
    40 `python-mock <http://python-mock.sourceforge.net/>`_ (last release 2005) are
    41 intentionally omitted.
    43 .. note::
    45     A more up to date, and tested for all mock libraries (only the mock
    46     examples on this page can be executed as doctests) version of this
    47     comparison is maintained by Gary Bernhardt:
    49     * `Python Mock Library Comparison
    50       <http://garybernhardt.github.com/python-mock-comparison/>`_
    52 This comparison is by no means complete, and also may not be fully idiomatic
    53 for all the libraries represented. *Please* contribute corrections, missing
    54 comparisons, or comparisons for additional libraries to the `mock issue
    55 tracker <https://code.google.com/p/mock/issues/list>`_.
    57 This comparison page was originally created by the `Mox project
    58 <https://code.google.com/p/pymox/wiki/MoxComparison>`_ and then extended for
    59 `flexmock and mock <http://has207.github.com/flexmock/compare.html>`_ by
    60 Herman Sheremetyev. Dingus examples written by `Gary Bernhadt
    61 <http://garybernhardt.github.com/python-mock-comparison/>`_. fudge examples
    62 provided by `Kumar McMillan <http://farmdev.com/>`_.
    64 .. note::
    66     The examples tasks here were originally created by Mox which is a mocking
    67     *framework* rather than a library like mock. The tasks shown naturally
    68     exemplify tasks that frameworks are good at and not the ones they make
    69     harder. In particular you can take a `Mock` or `MagicMock` object and use
    70     it in any way you want with no up-front configuration. The same is also
    71     true for Dingus.
    73     The examples for mock here assume version 0.7.0.
    76 Simple fake object
    77 ~~~~~~~~~~~~~~~~~~
    79 .. doctest::
    81     >>> # mock
    82     >>> my_mock = mock.Mock()
    83     >>> my_mock.some_method.return_value = "calculated value"
    84     >>> my_mock.some_attribute = "value"
    85     >>> assertEqual("calculated value", my_mock.some_method())
    86     >>> assertEqual("value", my_mock.some_attribute)
    88 ::
    90     # Flexmock
    91     mock = flexmock(some_method=lambda: "calculated value", some_attribute="value")
    92     assertEqual("calculated value", mock.some_method())
    93     assertEqual("value", mock.some_attribute)
    95     # Mox
    96     mock = mox.MockAnything()
    97     mock.some_method().AndReturn("calculated value")
    98     mock.some_attribute = "value"
    99     mox.Replay(mock)
   100     assertEqual("calculated value", mock.some_method())
   101     assertEqual("value", mock.some_attribute)
   103     # Mocker
   104     mock = mocker.mock()
   105     mock.some_method()
   106     mocker.result("calculated value")
   107     mocker.replay()
   108     mock.some_attribute = "value"
   109     assertEqual("calculated value", mock.some_method())
   110     assertEqual("value", mock.some_attribute)
   112 ::
   114     >>> # Dingus
   115     >>> my_dingus = dingus.Dingus(some_attribute="value",
   116     ...                           some_method__returns="calculated value")
   117     >>> assertEqual("calculated value", my_dingus.some_method())
   118     >>> assertEqual("value", my_dingus.some_attribute)
   120 ::
   122     >>> # fudge
   123     >>> my_fake = (fudge.Fake()
   124     ...            .provides('some_method')
   125     ...            .returns("calculated value")
   126     ...            .has_attr(some_attribute="value"))
   127     ...
   128     >>> assertEqual("calculated value", my_fake.some_method())
   129     >>> assertEqual("value", my_fake.some_attribute)
   132 Simple mock
   133 ~~~~~~~~~~~
   135 .. doctest::
   137     >>> # mock
   138     >>> my_mock = mock.Mock()
   139     >>> my_mock.some_method.return_value = "value"
   140     >>> assertEqual("value", my_mock.some_method())
   141     >>> my_mock.some_method.assert_called_once_with()
   143 ::
   145     # Flexmock
   146     mock = flexmock()
   147     mock.should_receive("some_method").and_return("value").once
   148     assertEqual("value", mock.some_method())
   150     # Mox
   151     mock = mox.MockAnything()
   152     mock.some_method().AndReturn("value")
   153     mox.Replay(mock)
   154     assertEqual("value", mock.some_method())
   155     mox.Verify(mock)
   157     # Mocker
   158     mock = mocker.mock()
   159     mock.some_method()
   160     mocker.result("value")
   161     mocker.replay()
   162     assertEqual("value", mock.some_method())
   163     mocker.verify()
   165 ::
   167     >>> # Dingus
   168     >>> my_dingus = dingus.Dingus(some_method__returns="value")
   169     >>> assertEqual("value", my_dingus.some_method())
   170     >>> assert my_dingus.some_method.calls().once()
   172 ::
   174     >>> # fudge
   175     >>> @fudge.test
   176     ... def test():
   177     ...     my_fake = (fudge.Fake()
   178     ...                .expects('some_method')
   179     ...                .returns("value")
   180     ...                .times_called(1))
   181     ...
   182     >>> test()
   183     Traceback (most recent call last):
   184     ...
   185     AssertionError: fake:my_fake.some_method() was not called
   188 Creating partial mocks
   189 ~~~~~~~~~~~~~~~~~~~~~~
   191 .. doctest::
   193     >>> # mock
   194     >>> SomeObject.some_method = mock.Mock(return_value='value')
   195     >>> assertEqual("value", SomeObject.some_method())
   197 ::
   199     # Flexmock
   200     flexmock(SomeObject).should_receive("some_method").and_return('value')
   201     assertEqual("value", mock.some_method())
   203     # Mox
   204     mock = mox.MockObject(SomeObject)
   205     mock.some_method().AndReturn("value")
   206     mox.Replay(mock)
   207     assertEqual("value", mock.some_method())
   208     mox.Verify(mock)
   210     # Mocker
   211     mock = mocker.mock(SomeObject)
   212     mock.Get()
   213     mocker.result("value")
   214     mocker.replay()
   215     assertEqual("value", mock.some_method())
   216     mocker.verify()
   218 ::
   220     >>> # Dingus
   221     >>> object = SomeObject
   222     >>> object.some_method = dingus.Dingus(return_value="value")
   223     >>> assertEqual("value", object.some_method())
   225 ::
   227     >>> # fudge
   228     >>> fake = fudge.Fake().is_callable().returns("<fudge-value>")
   229     >>> with fudge.patched_context(SomeObject, 'some_method', fake):
   230     ...     s = SomeObject()
   231     ...     assertEqual("<fudge-value>", s.some_method())
   232     ...
   235 Ensure calls are made in specific order
   236 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   238 .. doctest::
   240     >>> # mock
   241     >>> my_mock = mock.Mock(spec=SomeObject)
   242     >>> my_mock.method1()
   243     <Mock name='mock.method1()' id='...'>
   244     >>> my_mock.method2()
   245     <Mock name='mock.method2()' id='...'>
   246     >>> assertEqual(my_mock.mock_calls, [call.method1(), call.method2()])
   248 ::
   250     # Flexmock
   251     mock = flexmock(SomeObject)
   252     mock.should_receive('method1').once.ordered.and_return('first thing')
   253     mock.should_receive('method2').once.ordered.and_return('second thing')
   255     # Mox
   256     mock = mox.MockObject(SomeObject)
   257     mock.method1().AndReturn('first thing')
   258     mock.method2().AndReturn('second thing')
   259     mox.Replay(mock)
   260     mox.Verify(mock)
   262     # Mocker
   263     mock = mocker.mock()
   264     with mocker.order():
   265         mock.method1()
   266         mocker.result('first thing')
   267         mock.method2()
   268         mocker.result('second thing')
   269         mocker.replay()
   270         mocker.verify()
   272 ::
   274     >>> # Dingus
   275     >>> my_dingus = dingus.Dingus()
   276     >>> my_dingus.method1()
   277     <Dingus ...>
   278     >>> my_dingus.method2()
   279     <Dingus ...>
   280     >>> assertEqual(['method1', 'method2'], [call.name for call in my_dingus.calls])
   282 ::
   284     >>> # fudge
   285     >>> @fudge.test
   286     ... def test():
   287     ...     my_fake = (fudge.Fake()
   288     ...                .remember_order()
   289     ...                .expects('method1')
   290     ...                .expects('method2'))
   291     ...     my_fake.method2()
   292     ...     my_fake.method1()
   293     ...
   294     >>> test()
   295     Traceback (most recent call last):
   296     ...
   297     AssertionError: Call #1 was fake:my_fake.method2(); Expected: #1 fake:my_fake.method1(), #2 fake:my_fake.method2(), end
   300 Raising exceptions
   301 ~~~~~~~~~~~~~~~~~~
   303 .. doctest::
   305     >>> # mock
   306     >>> my_mock = mock.Mock()
   307     >>> my_mock.some_method.side_effect = SomeException("message")
   308     >>> assertRaises(SomeException, my_mock.some_method)
   310 ::
   312     # Flexmock
   313     mock = flexmock()
   314     mock.should_receive("some_method").and_raise(SomeException("message"))
   315     assertRaises(SomeException, mock.some_method)
   317     # Mox
   318     mock = mox.MockAnything()
   319     mock.some_method().AndRaise(SomeException("message"))
   320     mox.Replay(mock)
   321     assertRaises(SomeException, mock.some_method)
   322     mox.Verify(mock)
   324     # Mocker
   325     mock = mocker.mock()
   326     mock.some_method()
   327     mocker.throw(SomeException("message"))
   328     mocker.replay()
   329     assertRaises(SomeException, mock.some_method)
   330     mocker.verify()
   332 ::
   334     >>> # Dingus
   335     >>> my_dingus = dingus.Dingus()
   336     >>> my_dingus.some_method = dingus.exception_raiser(SomeException)
   337     >>> assertRaises(SomeException, my_dingus.some_method)
   339 ::
   341     >>> # fudge
   342     >>> my_fake = (fudge.Fake()
   343     ...            .is_callable()
   344     ...            .raises(SomeException("message")))
   345     ...
   346     >>> my_fake()
   347     Traceback (most recent call last):
   348     ...
   349     SomeException: message
   352 Override new instances of a class
   353 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   355 .. doctest::
   357     >>> # mock
   358     >>> with mock.patch('somemodule.Someclass') as MockClass:
   359     ...     MockClass.return_value = some_other_object
   360     ...     assertEqual(some_other_object, somemodule.Someclass())
   361     ...
   364 ::
   366     # Flexmock
   367     flexmock(some_module.SomeClass, new_instances=some_other_object)
   368     assertEqual(some_other_object, some_module.SomeClass())
   370     # Mox
   371     # (you will probably have mox.Mox() available as self.mox in a real test)
   372     mox.Mox().StubOutWithMock(some_module, 'SomeClass', use_mock_anything=True)
   373     some_module.SomeClass().AndReturn(some_other_object)
   374     mox.ReplayAll()
   375     assertEqual(some_other_object, some_module.SomeClass())
   377     # Mocker
   378     instance = mocker.mock()
   379     klass = mocker.replace(SomeClass, spec=None)
   380     klass('expected', 'args')
   381     mocker.result(instance)
   383 ::
   385     >>> # Dingus
   386     >>> MockClass = dingus.Dingus(return_value=some_other_object)
   387     >>> with dingus.patch('somemodule.SomeClass', MockClass):
   388     ...     assertEqual(some_other_object, somemodule.SomeClass())
   389     ...
   391 ::
   393     >>> # fudge
   394     >>> @fudge.patch('somemodule.SomeClass')
   395     ... def test(FakeClass):
   396     ...     FakeClass.is_callable().returns(some_other_object)
   397     ...     assertEqual(some_other_object, somemodule.SomeClass())
   398     ...
   399     >>> test()
   402 Call the same method multiple times
   403 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   405 .. note::
   407     You don't need to do *any* configuration to call `mock.Mock()` methods
   408     multiple times. Attributes like `call_count`, `call_args_list` and
   409     `method_calls` provide various different ways of making assertions about
   410     how the mock was used.
   412 .. doctest::
   414     >>> # mock
   415     >>> my_mock = mock.Mock()
   416     >>> my_mock.some_method()
   417     <Mock name='mock.some_method()' id='...'>
   418     >>> my_mock.some_method()
   419     <Mock name='mock.some_method()' id='...'>
   420     >>> assert my_mock.some_method.call_count >= 2
   422 ::
   424     # Flexmock # (verifies that the method gets called at least twice)
   425     flexmock(some_object).should_receive('some_method').at_least.twice
   427     # Mox
   428     # (does not support variable number of calls, so you need to create a new entry for each explicit call)
   429     mock = mox.MockObject(some_object)
   430     mock.some_method(mox.IgnoreArg(), mox.IgnoreArg())
   431     mock.some_method(mox.IgnoreArg(), mox.IgnoreArg())
   432     mox.Replay(mock)
   433     mox.Verify(mock)
   435     # Mocker
   436     # (TODO)
   438 ::
   440     >>> # Dingus
   441     >>> my_dingus = dingus.Dingus()
   442     >>> my_dingus.some_method()
   443     <Dingus ...>
   444     >>> my_dingus.some_method()
   445     <Dingus ...>
   446     >>> assert len(my_dingus.calls('some_method')) == 2
   448 ::
   450     >>> # fudge
   451     >>> @fudge.test
   452     ... def test():
   453     ...     my_fake = fudge.Fake().expects('some_method').times_called(2)
   454     ...     my_fake.some_method()
   455     ...
   456     >>> test()
   457     Traceback (most recent call last):
   458     ...
   459     AssertionError: fake:my_fake.some_method() was called 1 time(s). Expected 2.
   462 Mock chained methods
   463 ~~~~~~~~~~~~~~~~~~~~
   465 .. doctest::
   467     >>> # mock
   468     >>> my_mock = mock.Mock()
   469     >>> method3 = my_mock.method1.return_value.method2.return_value.method3
   470     >>> method3.return_value = 'some value'
   471     >>> assertEqual('some value', my_mock.method1().method2().method3(1, 2))
   472     >>> method3.assert_called_once_with(1, 2)
   474 ::
   476     # Flexmock
   477     # (intermediate method calls are automatically assigned to temporary fake objects
   478     # and can be called with any arguments)
   479     flexmock(some_object).should_receive(
   480         'method1.method2.method3'
   481     ).with_args(arg1, arg2).and_return('some value')
   482     assertEqual('some_value', some_object.method1().method2().method3(arg1, arg2))
   484 ::
   486     # Mox
   487     mock = mox.MockObject(some_object)
   488     mock2 = mox.MockAnything()
   489     mock3 = mox.MockAnything()
   490     mock.method1().AndReturn(mock1)
   491     mock2.method2().AndReturn(mock2)
   492     mock3.method3(arg1, arg2).AndReturn('some_value')
   493     self.mox.ReplayAll()
   494     assertEqual("some_value", some_object.method1().method2().method3(arg1, arg2))
   495     self.mox.VerifyAll()
   497     # Mocker
   498     # (TODO)
   500 ::
   502     >>> # Dingus
   503     >>> my_dingus = dingus.Dingus()
   504     >>> method3 = my_dingus.method1.return_value.method2.return_value.method3
   505     >>> method3.return_value = 'some value'
   506     >>> assertEqual('some value', my_dingus.method1().method2().method3(1, 2))
   507     >>> assert method3.calls('()', 1, 2).once()
   509 ::
   511     >>> # fudge
   512     >>> @fudge.test
   513     ... def test():
   514     ...     my_fake = fudge.Fake()
   515     ...     (my_fake
   516     ...      .expects('method1')
   517     ...      .returns_fake()
   518     ...      .expects('method2')
   519     ...      .returns_fake()
   520     ...      .expects('method3')
   521     ...      .with_args(1, 2)
   522     ...      .returns('some value'))
   523     ...     assertEqual('some value', my_fake.method1().method2().method3(1, 2))
   524     ...
   525     >>> test()
   528 Mocking a context manager
   529 ~~~~~~~~~~~~~~~~~~~~~~~~~
   531 Examples for mock, Dingus and fudge only (so far):
   533 .. doctest::
   535     >>> # mock
   536     >>> my_mock = mock.MagicMock()
   537     >>> with my_mock:
   538     ...     pass
   539     ...
   540     >>> my_mock.__enter__.assert_called_with()
   541     >>> my_mock.__exit__.assert_called_with(None, None, None)
   543 ::
   546     >>> # Dingus (nothing special here; all dinguses are "magic mocks")
   547     >>> my_dingus = dingus.Dingus()
   548     >>> with my_dingus:
   549     ...     pass
   550     ...
   551     >>> assert my_dingus.__enter__.calls()
   552     >>> assert my_dingus.__exit__.calls('()', None, None, None)
   554 ::
   556     >>> # fudge
   557     >>> my_fake = fudge.Fake().provides('__enter__').provides('__exit__')
   558     >>> with my_fake:
   559     ...     pass
   560     ...
   563 Mocking the builtin open used as a context manager
   564 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   566 Example for mock only (so far):
   568 .. doctest::
   570     >>> # mock
   571     >>> my_mock = mock.MagicMock()
   572     >>> with mock.patch('__builtin__.open', my_mock):
   573     ...     manager = my_mock.return_value.__enter__.return_value
   574     ...     manager.read.return_value = 'some data'
   575     ...     with open('foo') as h:
   576     ...         data = h.read()
   577     ...
   578     >>> data
   579     'some data'
   580     >>> my_mock.assert_called_once_with('foo')
   582 *or*:
   584 .. doctest::
   586     >>> # mock
   587     >>> with mock.patch('__builtin__.open') as my_mock:
   588     ...     my_mock.return_value.__enter__ = lambda s: s
   589     ...     my_mock.return_value.__exit__ = mock.Mock()
   590     ...     my_mock.return_value.read.return_value = 'some data'
   591     ...     with open('foo') as h:
   592     ...         data = h.read()
   593     ...
   594     >>> data
   595     'some data'
   596     >>> my_mock.assert_called_once_with('foo')
   598 ::
   600     >>> # Dingus
   601     >>> my_dingus = dingus.Dingus()
   602     >>> with dingus.patch('__builtin__.open', my_dingus):
   603     ...     file_ = open.return_value.__enter__.return_value
   604     ...     file_.read.return_value = 'some data'
   605     ...     with open('foo') as h:
   606     ...         data = f.read()
   607     ...
   608     >>> data
   609     'some data'
   610     >>> assert my_dingus.calls('()', 'foo').once()
   612 ::
   614     >>> # fudge
   615     >>> from contextlib import contextmanager
   616     >>> from StringIO import StringIO
   617     >>> @contextmanager
   618     ... def fake_file(filename):
   619     ...     yield StringIO('sekrets')
   620     ...
   621     >>> with fudge.patch('__builtin__.open') as fake_open:
   622     ...     fake_open.is_callable().calls(fake_file)
   623     ...     with open('/etc/password') as f:
   624     ...         data = f.read()
   625     ...
   626     fake:__builtin__.open
   627     >>> data
   628     'sekrets'

mercurial