summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/unittest/mock.py39
-rw-r--r--Lib/unittest/test/testmock/testasync.py32
-rw-r--r--Lib/unittest/test/testmock/testhelpers.py21
-rw-r--r--Misc/ACKS2
-rw-r--r--Misc/NEWS.d/next/Library/2019-07-19-20-13-48.bpo-37555.S5am28.rst2
5 files changed, 83 insertions, 13 deletions
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index a98decc..513cd5c 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -852,7 +852,8 @@ class NonCallableMock(Base):
else:
name, args, kwargs = _call
try:
- return name, sig.bind(*args, **kwargs)
+ bound_call = sig.bind(*args, **kwargs)
+ return call(name, bound_call.args, bound_call.kwargs)
except TypeError as e:
return e.with_traceback(None)
else:
@@ -901,9 +902,9 @@ class NonCallableMock(Base):
def _error_message():
msg = self._format_mock_failure_message(args, kwargs)
return msg
- expected = self._call_matcher((args, kwargs))
+ expected = self._call_matcher(_Call((args, kwargs), two=True))
actual = self._call_matcher(self.call_args)
- if expected != actual:
+ if actual != expected:
cause = expected if isinstance(expected, Exception) else None
raise AssertionError(_error_message()) from cause
@@ -963,10 +964,10 @@ class NonCallableMock(Base):
The assert passes if the mock has *ever* been called, unlike
`assert_called_with` and `assert_called_once_with` that only pass if
the call is the most recent one."""
- expected = self._call_matcher((args, kwargs))
+ expected = self._call_matcher(_Call((args, kwargs), two=True))
+ cause = expected if isinstance(expected, Exception) else None
actual = [self._call_matcher(c) for c in self.call_args_list]
- if expected not in actual:
- cause = expected if isinstance(expected, Exception) else None
+ if cause or expected not in _AnyComparer(actual):
expected_string = self._format_mock_call_signature(args, kwargs)
raise AssertionError(
'%s call not found' % expected_string
@@ -1019,6 +1020,22 @@ class NonCallableMock(Base):
return f"\n{prefix}: {safe_repr(self.mock_calls)}."
+class _AnyComparer(list):
+ """A list which checks if it contains a call which may have an
+ argument of ANY, flipping the components of item and self from
+ their traditional locations so that ANY is guaranteed to be on
+ the left."""
+ def __contains__(self, item):
+ for _call in self:
+ if len(item) != len(_call):
+ continue
+ if all([
+ expected == actual
+ for expected, actual in zip(item, _call)
+ ]):
+ return True
+ return False
+
def _try_iter(obj):
if obj is None:
@@ -2171,9 +2188,9 @@ class AsyncMockMixin(Base):
msg = self._format_mock_failure_message(args, kwargs, action='await')
return msg
- expected = self._call_matcher((args, kwargs))
+ expected = self._call_matcher(_Call((args, kwargs), two=True))
actual = self._call_matcher(self.await_args)
- if expected != actual:
+ if actual != expected:
cause = expected if isinstance(expected, Exception) else None
raise AssertionError(_error_message()) from cause
@@ -2192,10 +2209,10 @@ class AsyncMockMixin(Base):
"""
Assert the mock has ever been awaited with the specified arguments.
"""
- expected = self._call_matcher((args, kwargs))
+ expected = self._call_matcher(_Call((args, kwargs), two=True))
+ cause = expected if isinstance(expected, Exception) else None
actual = [self._call_matcher(c) for c in self.await_args_list]
- if expected not in actual:
- cause = expected if isinstance(expected, Exception) else None
+ if cause or expected not in _AnyComparer(actual):
expected_string = self._format_mock_call_signature(args, kwargs)
raise AssertionError(
'%s await not found' % expected_string
diff --git a/Lib/unittest/test/testmock/testasync.py b/Lib/unittest/test/testmock/testasync.py
index 5502342..87f3cfc 100644
--- a/Lib/unittest/test/testmock/testasync.py
+++ b/Lib/unittest/test/testmock/testasync.py
@@ -2,8 +2,8 @@ import asyncio
import inspect
import unittest
-from unittest.mock import (call, AsyncMock, patch, MagicMock, create_autospec,
- _AwaitEvent)
+from unittest.mock import (ANY, call, AsyncMock, patch, MagicMock,
+ create_autospec, _AwaitEvent)
def tearDownModule():
@@ -192,6 +192,10 @@ class AsyncAutospecTest(unittest.TestCase):
spec.assert_awaited_with(1, 2, c=3)
spec.assert_awaited()
+ with self.assertRaises(AssertionError):
+ spec.assert_any_await(e=1)
+
+
def test_patch_with_autospec(self):
async def test_async():
@@ -607,6 +611,30 @@ class AsyncMockAssert(unittest.TestCase):
asyncio.run(self._runnable_test('SomethingElse'))
self.mock.assert_has_awaits(calls)
+ def test_awaits_asserts_with_any(self):
+ class Foo:
+ def __eq__(self, other): pass
+
+ asyncio.run(self._runnable_test(Foo(), 1))
+
+ self.mock.assert_has_awaits([call(ANY, 1)])
+ self.mock.assert_awaited_with(ANY, 1)
+ self.mock.assert_any_await(ANY, 1)
+
+ def test_awaits_asserts_with_spec_and_any(self):
+ class Foo:
+ def __eq__(self, other): pass
+
+ mock_with_spec = AsyncMock(spec=Foo)
+
+ async def _custom_mock_runnable_test(*args):
+ await mock_with_spec(*args)
+
+ asyncio.run(_custom_mock_runnable_test(Foo(), 1))
+ mock_with_spec.assert_has_awaits([call(ANY, 1)])
+ mock_with_spec.assert_awaited_with(ANY, 1)
+ mock_with_spec.assert_any_await(ANY, 1)
+
def test_assert_has_awaits_ordered(self):
calls = [call('NormalFoo'), call('baz')]
with self.assertRaises(AssertionError):
diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py
index f3c7acb..9e7ec5d 100644
--- a/Lib/unittest/test/testmock/testhelpers.py
+++ b/Lib/unittest/test/testmock/testhelpers.py
@@ -64,7 +64,28 @@ class AnyTest(unittest.TestCase):
self.assertEqual(expected, mock.mock_calls)
self.assertEqual(mock.mock_calls, expected)
+ def test_any_no_spec(self):
+ # This is a regression test for bpo-37555
+ class Foo:
+ def __eq__(self, other): pass
+
+ mock = Mock()
+ mock(Foo(), 1)
+ mock.assert_has_calls([call(ANY, 1)])
+ mock.assert_called_with(ANY, 1)
+ mock.assert_any_call(ANY, 1)
+
+ def test_any_and_spec_set(self):
+ # This is a regression test for bpo-37555
+ class Foo:
+ def __eq__(self, other): pass
+
+ mock = Mock(spec=Foo)
+ mock(Foo(), 1)
+ mock.assert_has_calls([call(ANY, 1)])
+ mock.assert_called_with(ANY, 1)
+ mock.assert_any_call(ANY, 1)
class CallTest(unittest.TestCase):
diff --git a/Misc/ACKS b/Misc/ACKS
index f321131..71e61c3 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -503,6 +503,7 @@ Tomer Filiba
Segev Finer
Jeffrey Finkelstein
Russell Finn
+Neal Finne
Dan Finnie
Nils Fischbeck
Frederik Fix
@@ -1714,6 +1715,7 @@ Roger Upole
Daniel Urban
Michael Urman
Hector Urtubia
+Elizabeth Uselton
Lukas Vacek
Ville Vainio
Yann Vaginay
diff --git a/Misc/NEWS.d/next/Library/2019-07-19-20-13-48.bpo-37555.S5am28.rst b/Misc/NEWS.d/next/Library/2019-07-19-20-13-48.bpo-37555.S5am28.rst
new file mode 100644
index 0000000..16d1d62
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-07-19-20-13-48.bpo-37555.S5am28.rst
@@ -0,0 +1,2 @@
+Fix `NonCallableMock._call_matcher` returning tuple instead of `_Call` object
+when `self._spec_signature` exists. Patch by Elizabeth Uselton \ No newline at end of file