[Python-checkins] bpo-35047, unittest.mock: Better error messages on assert_called_xxx failures (GH-10090)

Victor Stinner webhook-mailer at python.org
Sun Oct 28 16:37:13 EDT 2018


https://github.com/python/cpython/commit/47d94241a383e2b8a2c40e81d12d40d5947fb170
commit: 47d94241a383e2b8a2c40e81d12d40d5947fb170
branch: master
author: Petter Strandmark <petter.strandmark at gmail.com>
committer: Victor Stinner <vstinner at redhat.com>
date: 2018-10-28T21:37:10+01:00
summary:

bpo-35047, unittest.mock: Better error messages on assert_called_xxx failures (GH-10090)

unittest.mock now includes mock calls in exception messages if
assert_not_called, assert_called_once, or assert_called_once_with
fails.

files:
A Misc/NEWS.d/next/Library/2018-10-25-09-59-00.bpo-35047.abbaa.rst
M Lib/unittest/mock.py
M Lib/unittest/test/testmock/testmock.py
M Misc/ACKS

diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index 1a6c1a606c5a..1977b870834b 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -30,6 +30,7 @@
 import sys
 import builtins
 from types import ModuleType
+from unittest.util import safe_repr
 from functools import wraps, partial
 
 
@@ -778,8 +779,10 @@ def assert_not_called(_mock_self):
         """
         self = _mock_self
         if self.call_count != 0:
-            msg = ("Expected '%s' to not have been called. Called %s times." %
-                   (self._mock_name or 'mock', self.call_count))
+            msg = ("Expected '%s' to not have been called. Called %s times.%s"
+                   % (self._mock_name or 'mock',
+                      self.call_count,
+                      self._calls_repr()))
             raise AssertionError(msg)
 
     def assert_called(_mock_self):
@@ -796,8 +799,10 @@ def assert_called_once(_mock_self):
         """
         self = _mock_self
         if not self.call_count == 1:
-            msg = ("Expected '%s' to have been called once. Called %s times." %
-                   (self._mock_name or 'mock', self.call_count))
+            msg = ("Expected '%s' to have been called once. Called %s times.%s"
+                   % (self._mock_name or 'mock',
+                      self.call_count,
+                      self._calls_repr()))
             raise AssertionError(msg)
 
     def assert_called_with(_mock_self, *args, **kwargs):
@@ -825,8 +830,10 @@ def assert_called_once_with(_mock_self, *args, **kwargs):
         with the specified arguments."""
         self = _mock_self
         if not self.call_count == 1:
-            msg = ("Expected '%s' to be called once. Called %s times." %
-                   (self._mock_name or 'mock', self.call_count))
+            msg = ("Expected '%s' to be called once. Called %s times.%s"
+                   % (self._mock_name or 'mock',
+                      self.call_count,
+                      self._calls_repr()))
             raise AssertionError(msg)
         return self.assert_called_with(*args, **kwargs)
 
@@ -847,8 +854,8 @@ def assert_has_calls(self, calls, any_order=False):
         if not any_order:
             if expected not in all_calls:
                 raise AssertionError(
-                    'Calls not found.\nExpected: %r\n'
-                    'Actual: %r' % (_CallList(calls), self.mock_calls)
+                    'Calls not found.\nExpected: %r%s'
+                    % (_CallList(calls), self._calls_repr(prefix="Actual"))
                 ) from cause
             return
 
@@ -909,6 +916,19 @@ def _get_child_mock(self, **kw):
         return klass(**kw)
 
 
+    def _calls_repr(self, prefix="Calls"):
+        """Renders self.mock_calls as a string.
+
+        Example: "\nCalls: [call(1), call(2)]."
+
+        If self.mock_calls is empty, an empty string is returned. The
+        output will be truncated if very long.
+        """
+        if not self.mock_calls:
+            return ""
+        return f"\n{prefix}: {safe_repr(self.mock_calls)}."
+
+
 
 def _try_iter(obj):
     if obj is None:
diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py
index c7bfa277b511..8cd284a6522d 100644
--- a/Lib/unittest/test/testmock/testmock.py
+++ b/Lib/unittest/test/testmock/testmock.py
@@ -1,4 +1,5 @@
 import copy
+import re
 import sys
 import tempfile
 
@@ -407,6 +408,14 @@ def test_assert_called_once_with(self):
             lambda: mock.assert_called_once_with('bob', 'bar', baz=2)
         )
 
+    def test_assert_called_once_with_call_list(self):
+        m = Mock()
+        m(1)
+        m(2)
+        self.assertRaisesRegex(AssertionError,
+            re.escape("Calls: [call(1), call(2)]"),
+            lambda: m.assert_called_once_with(2))
+
 
     def test_assert_called_once_with_function_spec(self):
         def f(a, b, c, d=None):
@@ -1250,6 +1259,13 @@ def test_assert_not_called(self):
         with self.assertRaises(AssertionError):
             m.hello.assert_not_called()
 
+    def test_assert_not_called_message(self):
+        m = Mock()
+        m(1, 2)
+        self.assertRaisesRegex(AssertionError,
+            re.escape("Calls: [call(1, 2)]"),
+            m.assert_not_called)
+
     def test_assert_called(self):
         m = Mock()
         with self.assertRaises(AssertionError):
@@ -1271,6 +1287,20 @@ def test_assert_called_once(self):
         with self.assertRaises(AssertionError):
             m.hello.assert_called_once()
 
+    def test_assert_called_once_message(self):
+        m = Mock()
+        m(1, 2)
+        m(3)
+        self.assertRaisesRegex(AssertionError,
+            re.escape("Calls: [call(1, 2), call(3)]"),
+            m.assert_called_once)
+
+    def test_assert_called_once_message_not_called(self):
+        m = Mock()
+        with self.assertRaises(AssertionError) as e:
+            m.assert_called_once()
+        self.assertNotIn("Calls:", str(e.exception))
+
     #Issue21256 printout of keyword args should be in deterministic order
     def test_sorted_call_signature(self):
         m = Mock()
diff --git a/Misc/ACKS b/Misc/ACKS
index 5014584b7bc0..043d604a3f96 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1570,6 +1570,7 @@ Daniel Stokes
 Michael Stone
 Serhiy Storchaka
 Ken Stox
+Petter Strandmark
 Charalampos Stratakis
 Dan Stromberg
 Donald Stufft
diff --git a/Misc/NEWS.d/next/Library/2018-10-25-09-59-00.bpo-35047.abbaa.rst b/Misc/NEWS.d/next/Library/2018-10-25-09-59-00.bpo-35047.abbaa.rst
new file mode 100644
index 000000000000..12eda27527d8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-10-25-09-59-00.bpo-35047.abbaa.rst
@@ -0,0 +1,3 @@
+``unittest.mock`` now includes mock calls in exception messages if
+``assert_not_called``, ``assert_called_once``, or ``assert_called_once_with``
+fails. Patch by Petter Strandmark.
\ No newline at end of file



More information about the Python-checkins mailing list