diff --git a/.travis.yml b/.travis.yml index 712e854..12f571e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,40 @@ language: python python: - - "3.6" + - "3.5" + +env: + - TOXENV=py26-pytest27 + - TOXENV=py26-pytest28 + - TOXENV=py26-pytest29 + - TOXENV=py26-pytest30 + - TOXENV=py27-pytest27 + - TOXENV=py27-pytest28 + - TOXENV=py27-pytest29 + - TOXENV=py27-pytest30 + - TOXENV=py33-pytest27 + - TOXENV=py33-pytest28 + - TOXENV=py33-pytest29 + - TOXENV=py33-pytest30 + - TOXENV=py34-pytest27 + - TOXENV=py34-pytest28 + - TOXENV=py34-pytest29 + - TOXENV=py34-pytest30 + - TOXENV=py35-pytest27 + - TOXENV=py35-pytest28 + - TOXENV=py35-pytest29 + - TOXENV=py35-pytest30 + - TOXENV=linting + +matrix: + include: + - env: TOXENV=py36-pytest27 + python: '3.6' + - env: TOXENV=py36-pytest28 + python: '3.6' + - env: TOXENV=py36-pytest29 + python: '3.6' + - env: TOXENV=py36-pytest30 + python: '3.6' install: - pip install tox coveralls @@ -14,6 +48,7 @@ after_success: deploy: provider: pypi user: nicoddemus + skip_upload_docs: true password: secure: bB4adUZVIkt31cmNklskyIDNehujKToGnStnlunp7P8CBF6CGeNqkYU17emAPvfZbTb/ClUpiO9r6AD1ej32Uyr+I8qUyhuYtHG3JGp+WRR/tw+ytAZIJ9i+PMjBv1RAdyLENJ/Tx0LKHKsABr8dQIieLFqKZJuT77f/5ZkvI/U= on: diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d3d530b..d7f9a0e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,15 @@ +1.6.0 +----- + +* The original assertions raised by the various ``Mock.assert_*`` methods + now appear in the failure message, in addition to the message obtained from + pytest introspection. + Thanks `@quodlibetor`_ for the initial patch (`#79`_). + +.. _@quodlibetor: https://github.com/quodlibetor + +.. _#79: https://github.com/pytest-dev/pytest-mock/pull/79 + 1.5.0 ----- diff --git a/README.rst b/README.rst index d703408..acc584f 100644 --- a/README.rst +++ b/README.rst @@ -126,14 +126,30 @@ the method, and uses py.test's own `advanced assertions`_ to return a better diff:: - m = mocker.patch.object(DS, 'create_char') - DS().create_char('Raistlin', class_='mag', gift=12) - > m.assert_called_once_with('Raistlin', class_='mage', gift=12) - E assert {'class_': 'mag', 'gift': 12} == {'class_': 'mage', 'gift': 12} - E Omitting 1 identical items, use -v to show - E Differing items: - E {'class_': 'mag'} != {'class_': 'mage'} + mocker = + + def test(mocker): + m = mocker.Mock() + m('fo') + > m.assert_called_once_with('', bar=4) + E AssertionError: Expected call: mock('', bar=4) + E Actual call: mock('fo') + E + E pytest introspection follows: + E + E Args: + E assert ('fo',) == ('',) + E At index 0 diff: 'fo' != '' E Use -v to get the full diff + E Kwargs: + E assert {} == {'bar': 4} + E Right contains more items: + E {'bar': 4} + E Use -v to get the full diff + + +test_foo.py:6: AssertionError +========================== 1 failed in 0.03 seconds =========================== This is useful when asserting mock calls with many/nested arguments and trying diff --git a/pytest_mock.py b/pytest_mock.py index f952710..e36ba82 100644 --- a/pytest_mock.py +++ b/pytest_mock.py @@ -2,6 +2,7 @@ import sys import pytest + from _pytest_mock_version import version __version__ = version @@ -174,13 +175,27 @@ def assert_wrapper(__wrapped_mock_method__, *args, **kwargs): __tracebackhide__ = True try: __wrapped_mock_method__(*args, **kwargs) + return except AssertionError as e: - __mock_self = args[0] - if __mock_self.call_args is not None: - actual_args, actual_kwargs = __mock_self.call_args - assert actual_args == args[1:] - assert actual_kwargs == kwargs - raise AssertionError(*e.args) + if getattr(e, '_mock_introspection_applied', 0): + msg = str(e) + else: + __mock_self = args[0] + msg = str(e) + if __mock_self.call_args is not None: + actual_args, actual_kwargs = __mock_self.call_args + msg += '\n\npytest introspection follows:\n' + try: + assert actual_args == args[1:] + except AssertionError as e: + msg += '\nArgs:\n' + str(e) + try: + assert actual_kwargs == kwargs + except AssertionError as e: + msg += '\nKwargs:\n' + str(e) + e = AssertionError(msg) + e._mock_introspection_applied = True + raise e def wrap_assert_not_called(*args, **kwargs): diff --git a/test_pytest_mock.py b/test_pytest_mock.py index a906be5..a192318 100644 --- a/test_pytest_mock.py +++ b/test_pytest_mock.py @@ -4,7 +4,6 @@ from contextlib import contextmanager import py.code - import pytest pytest_plugins = 'pytester' @@ -514,3 +513,29 @@ def runpytest_subprocess(testdir, *args): else: # pytest 2.7.X return testdir.runpytest(*args) + + +def test_detailed_introspection(testdir): + """Check that the "mock_use_standalone" is being used. + """ + testdir.makepyfile(""" + def test(mocker): + m = mocker.Mock() + m('fo') + m.assert_called_once_with('', bar=4) + """) + result = testdir.runpytest('-s') + result.stdout.fnmatch_lines([ + "*AssertionError: Expected call: mock('', bar=4)*", + "*Actual call: mock('fo')*", + "*pytest introspection follows:*", + '*Args:', + "*assert ('fo',) == ('',)", + "*At index 0 diff: 'fo' != ''*", + "*Use -v to get the full diff*", + "*Kwargs:*", + "*assert {} == {'bar': 4}*", + "*Right contains more items:*", + "*{'bar': 4}*", + "*Use -v to get the full diff*", + ])