Thu, 22 Jan 2015 13:21:57 +0100
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'