michael@0: ========================= michael@0: Mock Library Comparison michael@0: ========================= michael@0: michael@0: michael@0: .. testsetup:: michael@0: michael@0: def assertEqual(a, b): michael@0: assert a == b, ("%r != %r" % (a, b)) michael@0: michael@0: def assertRaises(Exc, func): michael@0: try: michael@0: func() michael@0: except Exc: michael@0: return michael@0: assert False, ("%s not raised" % Exc) michael@0: michael@0: sys.modules['somemodule'] = somemodule = mock.Mock(name='somemodule') michael@0: class SomeException(Exception): michael@0: some_method = method1 = method2 = None michael@0: some_other_object = SomeObject = SomeException michael@0: michael@0: michael@0: A side-by-side comparison of how to accomplish some basic tasks with mock and michael@0: some other popular Python mocking libraries and frameworks. michael@0: michael@0: These are: michael@0: michael@0: * `flexmock `_ michael@0: * `mox `_ michael@0: * `Mocker `_ michael@0: * `dingus `_ michael@0: * `fudge `_ michael@0: michael@0: Popular python mocking frameworks not yet represented here include michael@0: `MiniMock `_. michael@0: michael@0: `pMock `_ (last release 2004 and doesn't import michael@0: in recent versions of Python) and michael@0: `python-mock `_ (last release 2005) are michael@0: intentionally omitted. michael@0: michael@0: .. note:: michael@0: michael@0: A more up to date, and tested for all mock libraries (only the mock michael@0: examples on this page can be executed as doctests) version of this michael@0: comparison is maintained by Gary Bernhardt: michael@0: michael@0: * `Python Mock Library Comparison michael@0: `_ michael@0: michael@0: This comparison is by no means complete, and also may not be fully idiomatic michael@0: for all the libraries represented. *Please* contribute corrections, missing michael@0: comparisons, or comparisons for additional libraries to the `mock issue michael@0: tracker `_. michael@0: michael@0: This comparison page was originally created by the `Mox project michael@0: `_ and then extended for michael@0: `flexmock and mock `_ by michael@0: Herman Sheremetyev. Dingus examples written by `Gary Bernhadt michael@0: `_. fudge examples michael@0: provided by `Kumar McMillan `_. michael@0: michael@0: .. note:: michael@0: michael@0: The examples tasks here were originally created by Mox which is a mocking michael@0: *framework* rather than a library like mock. The tasks shown naturally michael@0: exemplify tasks that frameworks are good at and not the ones they make michael@0: harder. In particular you can take a `Mock` or `MagicMock` object and use michael@0: it in any way you want with no up-front configuration. The same is also michael@0: true for Dingus. michael@0: michael@0: The examples for mock here assume version 0.7.0. michael@0: michael@0: michael@0: Simple fake object michael@0: ~~~~~~~~~~~~~~~~~~ michael@0: michael@0: .. doctest:: michael@0: michael@0: >>> # mock michael@0: >>> my_mock = mock.Mock() michael@0: >>> my_mock.some_method.return_value = "calculated value" michael@0: >>> my_mock.some_attribute = "value" michael@0: >>> assertEqual("calculated value", my_mock.some_method()) michael@0: >>> assertEqual("value", my_mock.some_attribute) michael@0: michael@0: :: michael@0: michael@0: # Flexmock michael@0: mock = flexmock(some_method=lambda: "calculated value", some_attribute="value") michael@0: assertEqual("calculated value", mock.some_method()) michael@0: assertEqual("value", mock.some_attribute) michael@0: michael@0: # Mox michael@0: mock = mox.MockAnything() michael@0: mock.some_method().AndReturn("calculated value") michael@0: mock.some_attribute = "value" michael@0: mox.Replay(mock) michael@0: assertEqual("calculated value", mock.some_method()) michael@0: assertEqual("value", mock.some_attribute) michael@0: michael@0: # Mocker michael@0: mock = mocker.mock() michael@0: mock.some_method() michael@0: mocker.result("calculated value") michael@0: mocker.replay() michael@0: mock.some_attribute = "value" michael@0: assertEqual("calculated value", mock.some_method()) michael@0: assertEqual("value", mock.some_attribute) michael@0: michael@0: :: michael@0: michael@0: >>> # Dingus michael@0: >>> my_dingus = dingus.Dingus(some_attribute="value", michael@0: ... some_method__returns="calculated value") michael@0: >>> assertEqual("calculated value", my_dingus.some_method()) michael@0: >>> assertEqual("value", my_dingus.some_attribute) michael@0: michael@0: :: michael@0: michael@0: >>> # fudge michael@0: >>> my_fake = (fudge.Fake() michael@0: ... .provides('some_method') michael@0: ... .returns("calculated value") michael@0: ... .has_attr(some_attribute="value")) michael@0: ... michael@0: >>> assertEqual("calculated value", my_fake.some_method()) michael@0: >>> assertEqual("value", my_fake.some_attribute) michael@0: michael@0: michael@0: Simple mock michael@0: ~~~~~~~~~~~ michael@0: michael@0: .. doctest:: michael@0: michael@0: >>> # mock michael@0: >>> my_mock = mock.Mock() michael@0: >>> my_mock.some_method.return_value = "value" michael@0: >>> assertEqual("value", my_mock.some_method()) michael@0: >>> my_mock.some_method.assert_called_once_with() michael@0: michael@0: :: michael@0: michael@0: # Flexmock michael@0: mock = flexmock() michael@0: mock.should_receive("some_method").and_return("value").once michael@0: assertEqual("value", mock.some_method()) michael@0: michael@0: # Mox michael@0: mock = mox.MockAnything() michael@0: mock.some_method().AndReturn("value") michael@0: mox.Replay(mock) michael@0: assertEqual("value", mock.some_method()) michael@0: mox.Verify(mock) michael@0: michael@0: # Mocker michael@0: mock = mocker.mock() michael@0: mock.some_method() michael@0: mocker.result("value") michael@0: mocker.replay() michael@0: assertEqual("value", mock.some_method()) michael@0: mocker.verify() michael@0: michael@0: :: michael@0: michael@0: >>> # Dingus michael@0: >>> my_dingus = dingus.Dingus(some_method__returns="value") michael@0: >>> assertEqual("value", my_dingus.some_method()) michael@0: >>> assert my_dingus.some_method.calls().once() michael@0: michael@0: :: michael@0: michael@0: >>> # fudge michael@0: >>> @fudge.test michael@0: ... def test(): michael@0: ... my_fake = (fudge.Fake() michael@0: ... .expects('some_method') michael@0: ... .returns("value") michael@0: ... .times_called(1)) michael@0: ... michael@0: >>> test() michael@0: Traceback (most recent call last): michael@0: ... michael@0: AssertionError: fake:my_fake.some_method() was not called michael@0: michael@0: michael@0: Creating partial mocks michael@0: ~~~~~~~~~~~~~~~~~~~~~~ michael@0: michael@0: .. doctest:: michael@0: michael@0: >>> # mock michael@0: >>> SomeObject.some_method = mock.Mock(return_value='value') michael@0: >>> assertEqual("value", SomeObject.some_method()) michael@0: michael@0: :: michael@0: michael@0: # Flexmock michael@0: flexmock(SomeObject).should_receive("some_method").and_return('value') michael@0: assertEqual("value", mock.some_method()) michael@0: michael@0: # Mox michael@0: mock = mox.MockObject(SomeObject) michael@0: mock.some_method().AndReturn("value") michael@0: mox.Replay(mock) michael@0: assertEqual("value", mock.some_method()) michael@0: mox.Verify(mock) michael@0: michael@0: # Mocker michael@0: mock = mocker.mock(SomeObject) michael@0: mock.Get() michael@0: mocker.result("value") michael@0: mocker.replay() michael@0: assertEqual("value", mock.some_method()) michael@0: mocker.verify() michael@0: michael@0: :: michael@0: michael@0: >>> # Dingus michael@0: >>> object = SomeObject michael@0: >>> object.some_method = dingus.Dingus(return_value="value") michael@0: >>> assertEqual("value", object.some_method()) michael@0: michael@0: :: michael@0: michael@0: >>> # fudge michael@0: >>> fake = fudge.Fake().is_callable().returns("") michael@0: >>> with fudge.patched_context(SomeObject, 'some_method', fake): michael@0: ... s = SomeObject() michael@0: ... assertEqual("", s.some_method()) michael@0: ... michael@0: michael@0: michael@0: Ensure calls are made in specific order michael@0: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ michael@0: michael@0: .. doctest:: michael@0: michael@0: >>> # mock michael@0: >>> my_mock = mock.Mock(spec=SomeObject) michael@0: >>> my_mock.method1() michael@0: michael@0: >>> my_mock.method2() michael@0: michael@0: >>> assertEqual(my_mock.mock_calls, [call.method1(), call.method2()]) michael@0: michael@0: :: michael@0: michael@0: # Flexmock michael@0: mock = flexmock(SomeObject) michael@0: mock.should_receive('method1').once.ordered.and_return('first thing') michael@0: mock.should_receive('method2').once.ordered.and_return('second thing') michael@0: michael@0: # Mox michael@0: mock = mox.MockObject(SomeObject) michael@0: mock.method1().AndReturn('first thing') michael@0: mock.method2().AndReturn('second thing') michael@0: mox.Replay(mock) michael@0: mox.Verify(mock) michael@0: michael@0: # Mocker michael@0: mock = mocker.mock() michael@0: with mocker.order(): michael@0: mock.method1() michael@0: mocker.result('first thing') michael@0: mock.method2() michael@0: mocker.result('second thing') michael@0: mocker.replay() michael@0: mocker.verify() michael@0: michael@0: :: michael@0: michael@0: >>> # Dingus michael@0: >>> my_dingus = dingus.Dingus() michael@0: >>> my_dingus.method1() michael@0: michael@0: >>> my_dingus.method2() michael@0: michael@0: >>> assertEqual(['method1', 'method2'], [call.name for call in my_dingus.calls]) michael@0: michael@0: :: michael@0: michael@0: >>> # fudge michael@0: >>> @fudge.test michael@0: ... def test(): michael@0: ... my_fake = (fudge.Fake() michael@0: ... .remember_order() michael@0: ... .expects('method1') michael@0: ... .expects('method2')) michael@0: ... my_fake.method2() michael@0: ... my_fake.method1() michael@0: ... michael@0: >>> test() michael@0: Traceback (most recent call last): michael@0: ... michael@0: AssertionError: Call #1 was fake:my_fake.method2(); Expected: #1 fake:my_fake.method1(), #2 fake:my_fake.method2(), end michael@0: michael@0: michael@0: Raising exceptions michael@0: ~~~~~~~~~~~~~~~~~~ michael@0: michael@0: .. doctest:: michael@0: michael@0: >>> # mock michael@0: >>> my_mock = mock.Mock() michael@0: >>> my_mock.some_method.side_effect = SomeException("message") michael@0: >>> assertRaises(SomeException, my_mock.some_method) michael@0: michael@0: :: michael@0: michael@0: # Flexmock michael@0: mock = flexmock() michael@0: mock.should_receive("some_method").and_raise(SomeException("message")) michael@0: assertRaises(SomeException, mock.some_method) michael@0: michael@0: # Mox michael@0: mock = mox.MockAnything() michael@0: mock.some_method().AndRaise(SomeException("message")) michael@0: mox.Replay(mock) michael@0: assertRaises(SomeException, mock.some_method) michael@0: mox.Verify(mock) michael@0: michael@0: # Mocker michael@0: mock = mocker.mock() michael@0: mock.some_method() michael@0: mocker.throw(SomeException("message")) michael@0: mocker.replay() michael@0: assertRaises(SomeException, mock.some_method) michael@0: mocker.verify() michael@0: michael@0: :: michael@0: michael@0: >>> # Dingus michael@0: >>> my_dingus = dingus.Dingus() michael@0: >>> my_dingus.some_method = dingus.exception_raiser(SomeException) michael@0: >>> assertRaises(SomeException, my_dingus.some_method) michael@0: michael@0: :: michael@0: michael@0: >>> # fudge michael@0: >>> my_fake = (fudge.Fake() michael@0: ... .is_callable() michael@0: ... .raises(SomeException("message"))) michael@0: ... michael@0: >>> my_fake() michael@0: Traceback (most recent call last): michael@0: ... michael@0: SomeException: message michael@0: michael@0: michael@0: Override new instances of a class michael@0: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ michael@0: michael@0: .. doctest:: michael@0: michael@0: >>> # mock michael@0: >>> with mock.patch('somemodule.Someclass') as MockClass: michael@0: ... MockClass.return_value = some_other_object michael@0: ... assertEqual(some_other_object, somemodule.Someclass()) michael@0: ... michael@0: michael@0: michael@0: :: michael@0: michael@0: # Flexmock michael@0: flexmock(some_module.SomeClass, new_instances=some_other_object) michael@0: assertEqual(some_other_object, some_module.SomeClass()) michael@0: michael@0: # Mox michael@0: # (you will probably have mox.Mox() available as self.mox in a real test) michael@0: mox.Mox().StubOutWithMock(some_module, 'SomeClass', use_mock_anything=True) michael@0: some_module.SomeClass().AndReturn(some_other_object) michael@0: mox.ReplayAll() michael@0: assertEqual(some_other_object, some_module.SomeClass()) michael@0: michael@0: # Mocker michael@0: instance = mocker.mock() michael@0: klass = mocker.replace(SomeClass, spec=None) michael@0: klass('expected', 'args') michael@0: mocker.result(instance) michael@0: michael@0: :: michael@0: michael@0: >>> # Dingus michael@0: >>> MockClass = dingus.Dingus(return_value=some_other_object) michael@0: >>> with dingus.patch('somemodule.SomeClass', MockClass): michael@0: ... assertEqual(some_other_object, somemodule.SomeClass()) michael@0: ... michael@0: michael@0: :: michael@0: michael@0: >>> # fudge michael@0: >>> @fudge.patch('somemodule.SomeClass') michael@0: ... def test(FakeClass): michael@0: ... FakeClass.is_callable().returns(some_other_object) michael@0: ... assertEqual(some_other_object, somemodule.SomeClass()) michael@0: ... michael@0: >>> test() michael@0: michael@0: michael@0: Call the same method multiple times michael@0: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ michael@0: michael@0: .. note:: michael@0: michael@0: You don't need to do *any* configuration to call `mock.Mock()` methods michael@0: multiple times. Attributes like `call_count`, `call_args_list` and michael@0: `method_calls` provide various different ways of making assertions about michael@0: how the mock was used. michael@0: michael@0: .. doctest:: michael@0: michael@0: >>> # mock michael@0: >>> my_mock = mock.Mock() michael@0: >>> my_mock.some_method() michael@0: michael@0: >>> my_mock.some_method() michael@0: michael@0: >>> assert my_mock.some_method.call_count >= 2 michael@0: michael@0: :: michael@0: michael@0: # Flexmock # (verifies that the method gets called at least twice) michael@0: flexmock(some_object).should_receive('some_method').at_least.twice michael@0: michael@0: # Mox michael@0: # (does not support variable number of calls, so you need to create a new entry for each explicit call) michael@0: mock = mox.MockObject(some_object) michael@0: mock.some_method(mox.IgnoreArg(), mox.IgnoreArg()) michael@0: mock.some_method(mox.IgnoreArg(), mox.IgnoreArg()) michael@0: mox.Replay(mock) michael@0: mox.Verify(mock) michael@0: michael@0: # Mocker michael@0: # (TODO) michael@0: michael@0: :: michael@0: michael@0: >>> # Dingus michael@0: >>> my_dingus = dingus.Dingus() michael@0: >>> my_dingus.some_method() michael@0: michael@0: >>> my_dingus.some_method() michael@0: michael@0: >>> assert len(my_dingus.calls('some_method')) == 2 michael@0: michael@0: :: michael@0: michael@0: >>> # fudge michael@0: >>> @fudge.test michael@0: ... def test(): michael@0: ... my_fake = fudge.Fake().expects('some_method').times_called(2) michael@0: ... my_fake.some_method() michael@0: ... michael@0: >>> test() michael@0: Traceback (most recent call last): michael@0: ... michael@0: AssertionError: fake:my_fake.some_method() was called 1 time(s). Expected 2. michael@0: michael@0: michael@0: Mock chained methods michael@0: ~~~~~~~~~~~~~~~~~~~~ michael@0: michael@0: .. doctest:: michael@0: michael@0: >>> # mock michael@0: >>> my_mock = mock.Mock() michael@0: >>> method3 = my_mock.method1.return_value.method2.return_value.method3 michael@0: >>> method3.return_value = 'some value' michael@0: >>> assertEqual('some value', my_mock.method1().method2().method3(1, 2)) michael@0: >>> method3.assert_called_once_with(1, 2) michael@0: michael@0: :: michael@0: michael@0: # Flexmock michael@0: # (intermediate method calls are automatically assigned to temporary fake objects michael@0: # and can be called with any arguments) michael@0: flexmock(some_object).should_receive( michael@0: 'method1.method2.method3' michael@0: ).with_args(arg1, arg2).and_return('some value') michael@0: assertEqual('some_value', some_object.method1().method2().method3(arg1, arg2)) michael@0: michael@0: :: michael@0: michael@0: # Mox michael@0: mock = mox.MockObject(some_object) michael@0: mock2 = mox.MockAnything() michael@0: mock3 = mox.MockAnything() michael@0: mock.method1().AndReturn(mock1) michael@0: mock2.method2().AndReturn(mock2) michael@0: mock3.method3(arg1, arg2).AndReturn('some_value') michael@0: self.mox.ReplayAll() michael@0: assertEqual("some_value", some_object.method1().method2().method3(arg1, arg2)) michael@0: self.mox.VerifyAll() michael@0: michael@0: # Mocker michael@0: # (TODO) michael@0: michael@0: :: michael@0: michael@0: >>> # Dingus michael@0: >>> my_dingus = dingus.Dingus() michael@0: >>> method3 = my_dingus.method1.return_value.method2.return_value.method3 michael@0: >>> method3.return_value = 'some value' michael@0: >>> assertEqual('some value', my_dingus.method1().method2().method3(1, 2)) michael@0: >>> assert method3.calls('()', 1, 2).once() michael@0: michael@0: :: michael@0: michael@0: >>> # fudge michael@0: >>> @fudge.test michael@0: ... def test(): michael@0: ... my_fake = fudge.Fake() michael@0: ... (my_fake michael@0: ... .expects('method1') michael@0: ... .returns_fake() michael@0: ... .expects('method2') michael@0: ... .returns_fake() michael@0: ... .expects('method3') michael@0: ... .with_args(1, 2) michael@0: ... .returns('some value')) michael@0: ... assertEqual('some value', my_fake.method1().method2().method3(1, 2)) michael@0: ... michael@0: >>> test() michael@0: michael@0: michael@0: Mocking a context manager michael@0: ~~~~~~~~~~~~~~~~~~~~~~~~~ michael@0: michael@0: Examples for mock, Dingus and fudge only (so far): michael@0: michael@0: .. doctest:: michael@0: michael@0: >>> # mock michael@0: >>> my_mock = mock.MagicMock() michael@0: >>> with my_mock: michael@0: ... pass michael@0: ... michael@0: >>> my_mock.__enter__.assert_called_with() michael@0: >>> my_mock.__exit__.assert_called_with(None, None, None) michael@0: michael@0: :: michael@0: michael@0: michael@0: >>> # Dingus (nothing special here; all dinguses are "magic mocks") michael@0: >>> my_dingus = dingus.Dingus() michael@0: >>> with my_dingus: michael@0: ... pass michael@0: ... michael@0: >>> assert my_dingus.__enter__.calls() michael@0: >>> assert my_dingus.__exit__.calls('()', None, None, None) michael@0: michael@0: :: michael@0: michael@0: >>> # fudge michael@0: >>> my_fake = fudge.Fake().provides('__enter__').provides('__exit__') michael@0: >>> with my_fake: michael@0: ... pass michael@0: ... michael@0: michael@0: michael@0: Mocking the builtin open used as a context manager michael@0: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ michael@0: michael@0: Example for mock only (so far): michael@0: michael@0: .. doctest:: michael@0: michael@0: >>> # mock michael@0: >>> my_mock = mock.MagicMock() michael@0: >>> with mock.patch('__builtin__.open', my_mock): michael@0: ... manager = my_mock.return_value.__enter__.return_value michael@0: ... manager.read.return_value = 'some data' michael@0: ... with open('foo') as h: michael@0: ... data = h.read() michael@0: ... michael@0: >>> data michael@0: 'some data' michael@0: >>> my_mock.assert_called_once_with('foo') michael@0: michael@0: *or*: michael@0: michael@0: .. doctest:: michael@0: michael@0: >>> # mock michael@0: >>> with mock.patch('__builtin__.open') as my_mock: michael@0: ... my_mock.return_value.__enter__ = lambda s: s michael@0: ... my_mock.return_value.__exit__ = mock.Mock() michael@0: ... my_mock.return_value.read.return_value = 'some data' michael@0: ... with open('foo') as h: michael@0: ... data = h.read() michael@0: ... michael@0: >>> data michael@0: 'some data' michael@0: >>> my_mock.assert_called_once_with('foo') michael@0: michael@0: :: michael@0: michael@0: >>> # Dingus michael@0: >>> my_dingus = dingus.Dingus() michael@0: >>> with dingus.patch('__builtin__.open', my_dingus): michael@0: ... file_ = open.return_value.__enter__.return_value michael@0: ... file_.read.return_value = 'some data' michael@0: ... with open('foo') as h: michael@0: ... data = f.read() michael@0: ... michael@0: >>> data michael@0: 'some data' michael@0: >>> assert my_dingus.calls('()', 'foo').once() michael@0: michael@0: :: michael@0: michael@0: >>> # fudge michael@0: >>> from contextlib import contextmanager michael@0: >>> from StringIO import StringIO michael@0: >>> @contextmanager michael@0: ... def fake_file(filename): michael@0: ... yield StringIO('sekrets') michael@0: ... michael@0: >>> with fudge.patch('__builtin__.open') as fake_open: michael@0: ... fake_open.is_callable().calls(fake_file) michael@0: ... with open('/etc/password') as f: michael@0: ... data = f.read() michael@0: ... michael@0: fake:__builtin__.open michael@0: >>> data michael@0: 'sekrets'