[Python-checkins] Get mock coverage back to 100% (GH-18228)

Chris Withers webhook-mailer at python.org
Wed Jan 29 11:25:02 EST 2020


https://github.com/python/cpython/commit/db5e86adbce12350c26e7ffc2c6673369971a2dc
commit: db5e86adbce12350c26e7ffc2c6673369971a2dc
branch: master
author: Chris Withers <chris at withers.org>
committer: GitHub <noreply at github.com>
date: 2020-01-29T16:24:54Z
summary:

Get mock coverage back to 100% (GH-18228)

* use the `: pass` and `: yield` patterns for code that isn't expected to ever be executed.

* The _Call items passed to _AnyComparer are only ever of length two, so assert instead of if/else

* fix typo

* Fix bug, where stop-without-start patching dict blows up with `TypeError: 'NoneType' object is not iterable`, highlighted by lack of coverage of an except branch.

* The fix for bpo-37972 means _Call.count and _Call.index are no longer needed.

* add coverage for calling next() on a mock_open with readline.return_value set.

* __aiter__ is defined on the Mock so the one on _AsyncIterator is never called.

files:
M Lib/unittest/mock.py
M Lib/unittest/test/testmock/testasync.py
M Lib/unittest/test/testmock/testmock.py
M Lib/unittest/test/testmock/testpatch.py

diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index 37ea5c7e45c8b..9e692981a229b 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -1042,8 +1042,7 @@ class _AnyComparer(list):
     the left."""
     def __contains__(self, item):
         for _call in self:
-            if len(item) != len(_call):
-                continue
+            assert len(item) == len(_call)
             if all([
                 expected == actual
                 for expected, actual in zip(item, _call)
@@ -1856,7 +1855,8 @@ def _unpatch_dict(self):
 
     def __exit__(self, *args):
         """Unpatch the dict."""
-        self._unpatch_dict()
+        if self._original is not None:
+            self._unpatch_dict()
         return False
 
 
@@ -2168,7 +2168,7 @@ def __init__(self, /, *args, **kwargs):
         self.__dict__['__code__'] = code_mock
 
     async def _execute_mock_call(self, /, *args, **kwargs):
-        # This is nearly just like super(), except for sepcial handling
+        # This is nearly just like super(), except for special handling
         # of coroutines
 
         _call = self.call_args
@@ -2541,12 +2541,6 @@ def __getattribute__(self, attr):
         return tuple.__getattribute__(self, attr)
 
 
-    def count(self, /, *args, **kwargs):
-        return self.__getattr__('count')(*args, **kwargs)
-
-    def index(self, /, *args, **kwargs):
-        return self.__getattr__('index')(*args, **kwargs)
-
     def _get_call_arguments(self):
         if len(self) == 2:
             args, kwargs = self
@@ -2917,9 +2911,6 @@ def __init__(self, iterator):
         code_mock.co_flags = inspect.CO_ITERABLE_COROUTINE
         self.__dict__['__code__'] = code_mock
 
-    def __aiter__(self):
-        return self
-
     async def __anext__(self):
         try:
             return next(self.iterator)
diff --git a/Lib/unittest/test/testmock/testasync.py b/Lib/unittest/test/testmock/testasync.py
index 992076db78706..bf936220ccaf2 100644
--- a/Lib/unittest/test/testmock/testasync.py
+++ b/Lib/unittest/test/testmock/testasync.py
@@ -16,38 +16,28 @@ def tearDownModule():
 
 
 class AsyncClass:
-    def __init__(self):
-        pass
-    async def async_method(self):
-        pass
-    def normal_method(self):
-        pass
+    def __init__(self): pass
+    async def async_method(self): pass
+    def normal_method(self): pass
 
     @classmethod
-    async def async_class_method(cls):
-        pass
+    async def async_class_method(cls): pass
 
     @staticmethod
-    async def async_static_method():
-        pass
+    async def async_static_method(): pass
 
 
 class AwaitableClass:
-    def __await__(self):
-        yield
+    def __await__(self): yield
 
-async def async_func():
-    pass
+async def async_func(): pass
 
-async def async_func_args(a, b, *, c):
-    pass
+async def async_func_args(a, b, *, c): pass
 
-def normal_func():
-    pass
+def normal_func(): pass
 
 class NormalClass(object):
-    def a(self):
-        pass
+    def a(self): pass
 
 
 async_foo_name = f'{__name__}.AsyncClass'
@@ -402,8 +392,7 @@ def test_magicmock_lambda_spec(self):
 
 class AsyncArguments(IsolatedAsyncioTestCase):
     async def test_add_return_value(self):
-        async def addition(self, var):
-            return var + 1
+        async def addition(self, var): pass
 
         mock = AsyncMock(addition, return_value=10)
         output = await mock(5)
@@ -411,8 +400,7 @@ class AsyncArguments(IsolatedAsyncioTestCase):
         self.assertEqual(output, 10)
 
     async def test_add_side_effect_exception(self):
-        async def addition(var):
-            return var + 1
+        async def addition(var): pass
         mock = AsyncMock(addition, side_effect=Exception('err'))
         with self.assertRaises(Exception):
             await mock(5)
@@ -553,18 +541,14 @@ def test_magic_methods_are_async_functions(self):
 class AsyncContextManagerTest(unittest.TestCase):
 
     class WithAsyncContextManager:
-        async def __aenter__(self, *args, **kwargs):
-            return self
+        async def __aenter__(self, *args, **kwargs): pass
 
-        async def __aexit__(self, *args, **kwargs):
-            pass
+        async def __aexit__(self, *args, **kwargs): pass
 
     class WithSyncContextManager:
-        def __enter__(self, *args, **kwargs):
-            return self
+        def __enter__(self, *args, **kwargs): pass
 
-        def __exit__(self, *args, **kwargs):
-            pass
+        def __exit__(self, *args, **kwargs): pass
 
     class ProductionCode:
         # Example real-world(ish) code
@@ -673,16 +657,9 @@ class WithAsyncIterator(object):
         def __init__(self):
             self.items = ["foo", "NormalFoo", "baz"]
 
-        def __aiter__(self):
-            return self
-
-        async def __anext__(self):
-            try:
-                return self.items.pop()
-            except IndexError:
-                pass
+        def __aiter__(self): pass
 
-            raise StopAsyncIteration
+        async def __anext__(self): pass
 
     def test_aiter_set_return_value(self):
         mock_iter = AsyncMock(name="tester")
diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py
index 677346725bdd2..9b9e066cc545d 100644
--- a/Lib/unittest/test/testmock/testmock.py
+++ b/Lib/unittest/test/testmock/testmock.py
@@ -1868,6 +1868,11 @@ def test_mock_open_using_next(self):
         with self.assertRaises(StopIteration):
             next(f1)
 
+    def test_mock_open_next_with_readline_with_return_value(self):
+        mopen = mock.mock_open(read_data='foo\nbarn')
+        mopen.return_value.readline.return_value = 'abc'
+        self.assertEqual('abc', next(mopen()))
+
     def test_mock_open_write(self):
         # Test exception in file writing write()
         mock_namedtemp = mock.mock_open(mock.MagicMock(name='JLV'))
diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py
index 438dfd8cfbcc0..f1bc0e1cd40a2 100644
--- a/Lib/unittest/test/testmock/testpatch.py
+++ b/Lib/unittest/test/testmock/testpatch.py
@@ -770,6 +770,14 @@ def test_patch_dict_start_stop(self):
         self.assertEqual(d, original)
 
 
+    def test_patch_dict_stop_without_start(self):
+        d = {'foo': 'bar'}
+        original = d.copy()
+        patcher = patch.dict(d, [('spam', 'eggs')], clear=True)
+        self.assertEqual(patcher.stop(), False)
+        self.assertEqual(d, original)
+
+
     def test_patch_dict_class_decorator(self):
         this = self
         d = {'spam': 'eggs'}



More information about the Python-checkins mailing list