Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: descriptive mock assert wrap #58

Prev Previous commit
Next Next commit
Support assert_any_call assertion
- remove another debugging print
  • Loading branch information
asfaltboy committed Aug 4, 2016
commit 16a83155a27e976457557f7ced0098640288c0a2
55 changes: 44 additions & 11 deletions pytest_mock.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from pprint import pformat
import inspect

import pytest
import py

try:
import mock as mock_module
@@ -151,29 +153,60 @@ def mock(mocker):
_mock_module_originals = {}


DETAILED_ASSERTION = """{original}
DETAILED_ASSERTION = """{original!s}

... pytest introspection follows:
{detailed}
{detailed!s}
"""
FULL_ANY_CALLS_DIFF = "assert {call} not in {calls_list}"


def pytest_assertrepr_compare(config, op, left, right):
patch_enabled = config.getini('mock_traceback_monkeypatch')
if not patch_enabled:
return

if isinstance(left, mock_module._Call) and isinstance(right, mock_module._CallList) and op == "in":
u = py.builtin._totext
width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op
left_repr = py.io.saferepr(left, maxsize=int(width / 2))
right_repr = py.io.saferepr(right, maxsize=width - len(left_repr))

def ecu(s):
try:
return u(s, 'utf-8', 'replace')
except TypeError:
return s

summary = u('%s %s %s') % (ecu(left_repr), op, ecu(right_repr))
verbose = config.getoption('verbose')
if not verbose:
return [summary, u('Use -v to get the full diff')]
return [
summary, u('Full diff:'),
FULL_ANY_CALLS_DIFF.format(call=left, calls_list=pformat(right))
]


def assert_wrapper(__wrapped_mock_method__, *args, **kwargs):
__tracebackhide__ = True
try:
__wrapped_mock_method__(*args, **kwargs)
except AssertionError as e:
print(args, kwargs)
__mock_self = args[0]
__mock_self = args[0] # the mock instance
asserted_args = (args[1:], kwargs)
if __mock_self.call_args is not None:
actual_args, actual_kwargs = __mock_self.call_args
try:
assert (args[1:], kwargs) == (actual_args, actual_kwargs)
except AssertionError as pytest_diff:
msg = DETAILED_ASSERTION.format(original=e.msg,
detailed=pytest_diff.msg)
raise AssertionError(msg) # raise a new detailed exception
raise
if __wrapped_mock_method__.__name__ == 'assert_any_call':
assert mock_module.call(asserted_args) in __mock_self.call_args_list
else:
# compare tuples for deep pytest iterable diff
assert asserted_args == tuple(__mock_self.call_args)
except AssertionError as diff:
# raise a new detailed exception, while un-escaping line breaks
msg = DETAILED_ASSERTION.format(original=e, detailed=diff)
raise AssertionError(msg.encode().decode('unicode_escape'))
raise e


def wrap_assert_not_called(*args, **kwargs):
27 changes: 25 additions & 2 deletions test_pytest_mock.py
Original file line number Diff line number Diff line change
@@ -515,14 +515,37 @@ def test_assertion_error_is_descriptive(mocker):
mocker_mock(a=1, b=2)
mock_mock(a=1, b=2)

# arguments assertion for last call
try:
mocker_mock.assert_called_once_with(1, 2)
except AssertionError as e:
mocker_error_message = e.msg
mocker_called_once_with = e.msg
try:
mocker_mock.assert_called_with(1, 2)
except AssertionError as e:
mocker_called_with = e.msg

try:
assert_called_with(mock_mock, 1, 2)
except AssertionError as e:
mock_error_message = e.msg

assert mocker_error_message.startswith(mock_error_message)
assert mocker_called_once_with.startswith(mock_error_message)
mocker_mock(a='foo', b='bar')
assert mocker_called_with.startswith(mock_error_message)
assert "assert call((1, 2), {}) ==" in mocker_called_with

# argument assertion for any call (with multiline call list)
assert_any_call = _mock_module_originals['assert_any_call']
try:
mocker_mock.assert_any_call(1, 2)
except AssertionError as e:
mocker_any_call = e.msg

try:
assert_any_call(mock_mock, 1, 2)
except AssertionError as e:
mock_error_message = e.msg

assert mocker_any_call.startswith(mock_error_message)
assert "assert call((1, 2), {}) in [" in mocker_any_call