diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8f1c383..e7375ed 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,16 @@ +0.11.1 +------ + +* ``mocker.stub()`` now allows passing in the name for the constructed `Mock + `_ + object instead of having to set it using the internal ``_mock_name`` attribute + directly. This is useful for debugging as the name is used in the mock's + ``repr`` string as well as related assertion failure messages. + Thanks `@jurko-gospodnetic`_ for the PR (`#40`_). + +.. _@jurko-gospodnetic: https://github.com/jurko-gospodnetic +.. _#40: https://github.com/pytest-dev/pytest-mock/issues/40 + 0.11.0 ------ diff --git a/README.rst b/README.rst index e510f64..918043e 100644 --- a/README.rst +++ b/README.rst @@ -119,6 +119,7 @@ Stub *New in version 0.6* The stub is a mock object that accepts any arguments and is useful to test callbacks, for instance. +May be passed a name to be used by the constructed stub object in its repr (useful for debugging). .. code-block:: python @@ -126,7 +127,7 @@ The stub is a mock object that accepts any arguments and is useful to test callb def foo(on_something): on_something('foo', 'bar') - stub = mocker.stub() + stub = mocker.stub(name='on_something_stub') foo(stub) stub.assert_called_once_with('foo', 'bar') diff --git a/pytest_mock.py b/pytest_mock.py index 63e2a82..ecbae66 100644 --- a/pytest_mock.py +++ b/pytest_mock.py @@ -70,15 +70,16 @@ def spy(self, obj, name): autospec=autospec) return result - def stub(self): + def stub(self, name=None): """ Creates a stub method. It accepts any arguments. Ideal to register to callbacks in tests. + :param name: the constructed stub's name as used in repr :rtype: mock.MagicMock :return: Stub object. """ - return mock_module.MagicMock(spec=lambda *args, **kwargs: None) + return mock_module.MagicMock(spec=lambda *args, **kwargs: None, name=name) class _Patcher(object): """ diff --git a/test_pytest_mock.py b/test_pytest_mock.py index 703d165..72ef43b 100644 --- a/test_pytest_mock.py +++ b/test_pytest_mock.py @@ -168,14 +168,35 @@ def test_mocker_resetall(mocker): assert not open.called -def test_mocker_stub(mocker): - def foo(on_something): - on_something('foo', 'bar') - - stub = mocker.stub() - - foo(stub) - stub.assert_called_once_with('foo', 'bar') +class TestMockerStub: + def test_call(self, mocker): + stub = mocker.stub() + stub('foo', 'bar') + stub.assert_called_once_with('foo', 'bar') + + def test_repr_with_no_name(self, mocker): + stub = mocker.stub() + assert not 'name' in repr(stub) + + def test_repr_with_name(self, mocker): + test_name = 'funny walk' + stub = mocker.stub(name=test_name) + assert "name={0!r}".format(test_name) in repr(stub) + + def __test_failure_message(self, mocker, **kwargs): + expected_name = kwargs.get('name') or 'mock' + expected_message = 'Expected call: {0}()\nNot called'.format(expected_name) + stub = mocker.stub(**kwargs) + with pytest.raises(AssertionError) as exc_info: + stub.assert_called_with() + assert exc_info.value.msg == expected_message + + def test_failure_message_with_no_name(self, mocker): + self.__test_failure_message(mocker) + + @pytest.mark.parametrize('name', (None, '', 'f', 'The Castle of aaarrrrggh')) + def test_failure_message_with_name(self, mocker, name): + self.__test_failure_message(mocker, name=name) def test_instance_method_spy(mocker):