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.
This commit is contained in:
Chris Withers 2020-01-29 16:24:54 +00:00 committed by GitHub
parent a327677905
commit db5e86adbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 35 additions and 54 deletions

View File

@ -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 @@ class _patch_dict(object):
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 @@ class AsyncMockMixin(Base):
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 @@ class _Call(tuple):
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 @@ class _AsyncIterator:
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)

View File

@ -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 @@ class AsyncSpecSetTest(unittest.TestCase):
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 @@ class AsyncMagicMethods(unittest.TestCase):
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 AsyncIteratorTest(unittest.TestCase):
def __init__(self):
self.items = ["foo", "NormalFoo", "baz"]
def __aiter__(self):
return self
def __aiter__(self): pass
async def __anext__(self):
try:
return self.items.pop()
except IndexError:
pass
raise StopAsyncIteration
async def __anext__(self): pass
def test_aiter_set_return_value(self):
mock_iter = AsyncMock(name="tester")

View File

@ -1868,6 +1868,11 @@ class MockTest(unittest.TestCase):
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'))

View File

@ -770,6 +770,14 @@ class PatchTest(unittest.TestCase):
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'}