Commit adbf178e authored by Chris Withers's avatar Chris Withers Committed by GitHub

Mock 100% coverage (GH-13045)

This was achieved by:
* moving many pass statements in tests onto their own lines, so they pass line coverage and can match an easy ignore pattern if branch coverage is added later.
* removing code that cannot be reached.
* removing long-disabled tests.
* removing unused code.
* adding tests for uncovered code

It turned out that removing `if __name__ == '__main__'` blocks that run unittest.main() at the bottom of test files was surprisingly contentious, so they remain and can be filtered out with an appropriate .coveragerc.
parent b7378d77
......@@ -63,10 +63,7 @@ def _get_signature_object(func, as_instance, eat_self):
"""
if isinstance(func, type) and not as_instance:
# If it's a type and should be modelled as a type, use __init__.
try:
func = func.__init__
except AttributeError:
return None
func = func.__init__
# Skip the `self` argument in __init__
eat_self = True
elif not isinstance(func, FunctionTypes):
......@@ -147,8 +144,6 @@ def _set_signature(mock, original, instance=False):
# creates a function with signature (*args, **kwargs) that delegates to a
# mock. It still does signature checking by calling a lambda with the same
# signature as the original.
if not _callable(original):
return
skipfirst = isinstance(original, type)
result = _get_signature_object(original, instance, skipfirst)
......@@ -175,10 +170,6 @@ def _set_signature(mock, original, instance=False):
def _setup_func(funcopy, mock, sig):
funcopy.mock = mock
# can't use isinstance with mocks
if not _is_instance_mock(mock):
return
def assert_called_with(*args, **kwargs):
return mock.assert_called_with(*args, **kwargs)
def assert_called(*args, **kwargs):
......@@ -263,12 +254,6 @@ _missing = sentinel.MISSING
_deleted = sentinel.DELETED
def _copy(value):
if type(value) in (dict, list, tuple, set):
return type(value)(value)
return value
_allowed_names = {
'return_value', '_mock_return_value', 'side_effect',
'_mock_side_effect', '_mock_parent', '_mock_new_parent',
......@@ -351,8 +336,6 @@ def _check_and_set_parent(parent, value, name, new_name):
class _MockIter(object):
def __init__(self, obj):
self.obj = iter(obj)
def __iter__(self):
return self
def __next__(self):
return next(self.obj)
......@@ -452,7 +435,7 @@ class NonCallableMock(Base):
if isinstance(spec, type):
_spec_class = spec
else:
_spec_class = _get_class(spec)
_spec_class = type(spec)
res = _get_signature_object(spec,
_spec_as_instance, _eat_self)
_spec_signature = res and res[1]
......@@ -624,7 +607,7 @@ class NonCallableMock(Base):
dot = '.'
if _name_list == ['()']:
dot = ''
seen = set()
while _parent is not None:
last = _parent
......@@ -635,11 +618,6 @@ class NonCallableMock(Base):
_parent = _parent._mock_new_parent
# use ids here so as not to call __hash__ on the mocks
if id(_parent) in seen:
break
seen.add(id(_parent))
_name_list = list(reversed(_name_list))
_first = last._mock_name or 'mock'
if len(_name_list) > 1:
......@@ -753,8 +731,6 @@ class NonCallableMock(Base):
message = 'expected call not found.\nExpected: %s\nActual: %s'
expected_string = self._format_mock_call_signature(args, kwargs)
call_args = self.call_args
if len(call_args) == 3:
call_args = call_args[1:]
actual_string = self._format_mock_call_signature(*call_args)
return message % (expected_string, actual_string)
......@@ -992,8 +968,6 @@ class CallableMixin(Base):
self.call_args = _call
self.call_args_list.append(_call)
seen = set()
# initial stuff for method_calls:
do_method_calls = self._mock_parent is not None
method_call_name = self._mock_name
......@@ -1029,13 +1003,6 @@ class CallableMixin(Base):
# follow the parental chain:
_new_parent = _new_parent._mock_new_parent
# check we're not in an infinite loop:
# ( use ids here so as not to call __hash__ on the mocks)
_new_parent_id = id(_new_parent)
if _new_parent_id in seen:
break
seen.add(_new_parent_id)
effect = self.side_effect
if effect is not None:
if _is_exception(effect):
......@@ -1858,12 +1825,7 @@ def _set_return_value(mock, method, name):
return_calulator = _calculate_return_value.get(name)
if return_calulator is not None:
try:
return_value = return_calulator(mock)
except AttributeError:
# XXXX why do we return AttributeError here?
# set it as a side_effect instead?
return_value = AttributeError(name)
return_value = return_calulator(mock)
method.return_value = return_value
return
......@@ -1943,10 +1905,6 @@ class MagicProxy(object):
self.name = name
self.parent = parent
def __call__(self, *args, **kwargs):
m = self.create_mock()
return m(*args, **kwargs)
def create_mock(self):
entry = self.name
parent = self.parent
......@@ -2330,19 +2288,10 @@ def _must_skip(spec, entry, is_type):
else:
return False
# shouldn't get here unless function is a dynamically provided attribute
# XXXX untested behaviour
# function is a dynamically provided attribute
return is_type
def _get_class(obj):
try:
return obj.__class__
except AttributeError:
# it is possible for objects to have no __class__
return type(obj)
class _SpecState(object):
def __init__(self, spec, spec_set=False, parent=None,
......
......@@ -9,8 +9,7 @@ def is_instance(obj, klass):
class SomeClass(object):
class_attribute = None
def wibble(self):
pass
def wibble(self): pass
class X(object):
......
......@@ -98,8 +98,7 @@ class TestCallable(unittest.TestCase):
def test_patch_spec_callable_class(self):
class CallableX(X):
def __call__(self):
pass
def __call__(self): pass
class Sub(CallableX):
pass
......
This diff is collapsed.
......@@ -305,8 +305,7 @@ class TestMockingMagicMethods(unittest.TestCase):
def test_magic_methods_and_spec(self):
class Iterable(object):
def __iter__(self):
pass
def __iter__(self): pass
mock = Mock(spec=Iterable)
self.assertRaises(AttributeError, lambda: mock.__iter__)
......@@ -330,8 +329,7 @@ class TestMockingMagicMethods(unittest.TestCase):
def test_magic_methods_and_spec_set(self):
class Iterable(object):
def __iter__(self):
pass
def __iter__(self): pass
mock = Mock(spec_set=Iterable)
self.assertRaises(AttributeError, lambda: mock.__iter__)
......
......@@ -28,16 +28,13 @@ class Iter(object):
class Something(object):
def meth(self, a, b, c, d=None):
pass
def meth(self, a, b, c, d=None): pass
@classmethod
def cmeth(cls, a, b, c, d=None):
pass
def cmeth(cls, a, b, c, d=None): pass
@staticmethod
def smeth(a, b, c, d=None):
pass
def smeth(a, b, c, d=None): pass
class MockTest(unittest.TestCase):
......@@ -83,6 +80,21 @@ class MockTest(unittest.TestCase):
"return value in constructor not honoured")
def test_change_return_value_via_delegate(self):
def f(): pass
mock = create_autospec(f)
mock.mock.return_value = 1
self.assertEqual(mock(), 1)
def test_change_side_effect_via_delegate(self):
def f(): pass
mock = create_autospec(f)
mock.mock.side_effect = TypeError()
with self.assertRaises(TypeError):
mock()
def test_repr(self):
mock = Mock(name='foo')
self.assertIn('foo', repr(mock))
......@@ -161,8 +173,7 @@ class MockTest(unittest.TestCase):
results = [1, 2, 3]
def effect():
return results.pop()
def f():
pass
def f(): pass
mock = create_autospec(f)
mock.side_effect = [1, 2, 3]
......@@ -177,8 +188,7 @@ class MockTest(unittest.TestCase):
def test_autospec_side_effect_exception(self):
# Test for issue 23661
def f():
pass
def f(): pass
mock = create_autospec(f)
mock.side_effect = ValueError('Bazinga!')
......@@ -340,8 +350,7 @@ class MockTest(unittest.TestCase):
def test_assert_called_with_function_spec(self):
def f(a, b, c, d=None):
pass
def f(a, b, c, d=None): pass
mock = Mock(spec=f)
......@@ -409,8 +418,7 @@ class MockTest(unittest.TestCase):
def test_assert_called_once_with_function_spec(self):
def f(a, b, c, d=None):
pass
def f(a, b, c, d=None): pass
mock = Mock(spec=f)
......@@ -514,8 +522,7 @@ class MockTest(unittest.TestCase):
class Something(object):
x = 3
__something__ = None
def y(self):
pass
def y(self): pass
def test_attributes(mock):
# should work
......@@ -601,8 +608,7 @@ class MockTest(unittest.TestCase):
def test_customize_wrapped_object_with_side_effect_iterable(self):
class Real(object):
def method(self):
raise NotImplementedError()
def method(self): pass
real = Real()
mock = Mock(wraps=real)
......@@ -615,8 +621,7 @@ class MockTest(unittest.TestCase):
def test_customize_wrapped_object_with_side_effect_exception(self):
class Real(object):
def method(self):
raise NotImplementedError()
def method(self): pass
real = Real()
mock = Mock(wraps=real)
......@@ -627,9 +632,7 @@ class MockTest(unittest.TestCase):
def test_customize_wrapped_object_with_side_effect_function(self):
class Real(object):
def method(self):
raise NotImplementedError()
def method(self): pass
def side_effect():
return sentinel.VALUE
......@@ -642,8 +645,7 @@ class MockTest(unittest.TestCase):
def test_customize_wrapped_object_with_return_value(self):
class Real(object):
def method(self):
raise NotImplementedError()
def method(self): pass
real = Real()
mock = Mock(wraps=real)
......@@ -655,8 +657,7 @@ class MockTest(unittest.TestCase):
def test_customize_wrapped_object_with_return_value_and_side_effect(self):
# side_effect should always take precedence over return_value.
class Real(object):
def method(self):
raise NotImplementedError()
def method(self): pass
real = Real()
mock = Mock(wraps=real)
......@@ -671,8 +672,7 @@ class MockTest(unittest.TestCase):
def test_customize_wrapped_object_with_return_value_and_side_effect2(self):
# side_effect can return DEFAULT to default to return_value
class Real(object):
def method(self):
raise NotImplementedError()
def method(self): pass
real = Real()
mock = Mock(wraps=real)
......@@ -684,8 +684,7 @@ class MockTest(unittest.TestCase):
def test_customize_wrapped_object_with_return_value_and_side_effect_default(self):
class Real(object):
def method(self):
raise NotImplementedError()
def method(self): pass
real = Real()
mock = Mock(wraps=real)
......@@ -764,6 +763,26 @@ class MockTest(unittest.TestCase):
self.assertIsInstance(mock, X)
def test_spec_class_no_object_base(self):
class X:
pass
mock = Mock(spec=X)
self.assertIsInstance(mock, X)
mock = Mock(spec=X())
self.assertIsInstance(mock, X)
self.assertIs(mock.__class__, X)
self.assertEqual(Mock().__class__.__name__, 'Mock')
mock = Mock(spec_set=X)
self.assertIsInstance(mock, X)
mock = Mock(spec_set=X())
self.assertIsInstance(mock, X)
def test_setting_attribute_with_spec_set(self):
class X(object):
y = 3
......@@ -902,15 +921,9 @@ class MockTest(unittest.TestCase):
def assertRaisesWithMsg(self, exception, message, func, *args, **kwargs):
# needed because assertRaisesRegex doesn't work easily with newlines
try:
with self.assertRaises(exception) as context:
func(*args, **kwargs)
except:
instance = sys.exc_info()[1]
self.assertIsInstance(instance, exception)
else:
self.fail('Exception %r not raised' % (exception,))
msg = str(instance)
msg = str(context.exception)
self.assertEqual(msg, message)
......@@ -1099,6 +1112,18 @@ class MockTest(unittest.TestCase):
self.assertEqual(repr(m.mock_calls[2]), 'call.foo().bar().baz.bob()')
def test_mock_call_repr_loop(self):
m = Mock()
m.foo = m
repr(m.foo())
self.assertRegex(repr(m.foo()), r"<Mock name='mock\(\)' id='\d+'>")
def test_mock_calls_contains(self):
m = Mock()
self.assertFalse([call()] in m.mock_calls)
def test_subclassing(self):
class Subclass(Mock):
pass
......@@ -1312,8 +1337,7 @@ class MockTest(unittest.TestCase):
def test_assert_has_calls_with_function_spec(self):
def f(a, b, c, d=None):
pass
def f(a, b, c, d=None): pass
mock = Mock(spec=f)
......@@ -1371,8 +1395,7 @@ class MockTest(unittest.TestCase):
def test_assert_any_call_with_function_spec(self):
def f(a, b, c, d=None):
pass
def f(a, b, c, d=None): pass
mock = Mock(spec=f)
......@@ -1391,8 +1414,7 @@ class MockTest(unittest.TestCase):
def test_mock_calls_create_autospec(self):
def f(a, b):
pass
def f(a, b): pass
obj = Iter()
obj.f = f
......@@ -1417,12 +1439,10 @@ class MockTest(unittest.TestCase):
def test_create_autospec_classmethod_and_staticmethod(self):
class TestClass:
@classmethod
def class_method(cls):
pass
def class_method(cls): pass
@staticmethod
def static_method():
pass
def static_method(): pass
for method in ('class_method', 'static_method'):
with self.subTest(method=method):
mock_method = mock.create_autospec(getattr(TestClass, method))
......@@ -1848,8 +1868,7 @@ class MockTest(unittest.TestCase):
def test_parent_propagation_with_create_autospec(self):
def foo(a, b):
pass
def foo(a, b): pass
mock = Mock()
mock.child = create_autospec(foo)
......@@ -1878,11 +1897,12 @@ class MockTest(unittest.TestCase):
with patch.dict('sys.modules'):
del sys.modules['unittest.mock']
def trace(frame, event, arg):
# This trace will stop coverage being measured ;-)
def trace(frame, event, arg): # pragma: no cover
return trace
self.addCleanup(sys.settrace, sys.gettrace())
sys.settrace(trace)
self.addCleanup(sys.settrace, None)
from unittest.mock import (
Mock, MagicMock, NonCallableMock, NonCallableMagicMock
......
......@@ -43,31 +43,24 @@ something_else = sentinel.SomethingElse
class Foo(object):
def __init__(self, a):
pass
def f(self, a):
pass
def g(self):
pass
def __init__(self, a): pass
def f(self, a): pass
def g(self): pass
foo = 'bar'
@staticmethod
def static_method():
return 24
def static_method(): pass
@classmethod
def class_method(cls):
return 42
def class_method(cls): pass
class Bar(object):
def a(self):
pass
def a(self): pass
foo_name = '%s.Foo' % __name__
def function(a, b=Foo):
pass
def function(a, b=Foo): pass
class Container(object):
......@@ -370,31 +363,19 @@ class PatchTest(unittest.TestCase):
def test_patch_wont_create_by_default(self):
try:
with self.assertRaises(AttributeError):
@patch('%s.frooble' % builtin_string, sentinel.Frooble)
def test():
self.assertEqual(frooble, sentinel.Frooble)
def test(): pass
test()
except AttributeError:
pass
else:
self.fail('Patching non existent attributes should fail')
self.assertRaises(NameError, lambda: frooble)
def test_patchobject_wont_create_by_default(self):
try:
with self.assertRaises(AttributeError):
@patch.object(SomeClass, 'ord', sentinel.Frooble)
def test():
self.fail('Patching non existent attributes should fail')
def test(): pass
test()
except AttributeError:
pass
else:
self.fail('Patching non existent attributes should fail')
self.assertFalse(hasattr(SomeClass, 'ord'))
......@@ -484,6 +465,9 @@ class PatchTest(unittest.TestCase):
attribute = sentinel.Original
class Foo(object):
test_class_attr = 'whatever'
def test_method(other_self, mock_something):
self.assertEqual(PTModule.something, mock_something,
"unpatched")
......@@ -642,8 +626,7 @@ class PatchTest(unittest.TestCase):
@patch('%s.SomeClass' % __name__, object(), autospec=True)
@patch.object(SomeClass, object())
@patch.dict(foo)
def some_name():
pass
def some_name(): pass
self.assertEqual(some_name.__name__, 'some_name')
......@@ -654,12 +637,9 @@ class PatchTest(unittest.TestCase):
@patch.dict(foo, {'a': 'b'})
def test():
raise NameError('Konrad')
try:
with self.assertRaises(NameError):
test()
except NameError:
pass
else:
self.fail('NameError not raised by test')
self.assertEqual(foo, {})
......@@ -689,49 +669,6 @@ class PatchTest(unittest.TestCase):
support.target = original
def test_patch_descriptor(self):
# would be some effort to fix this - we could special case the
# builtin descriptors: classmethod, property, staticmethod
return
class Nothing(object):
foo = None
class Something(object):
foo = {}
@patch.object(Nothing, 'foo', 2)
@classmethod
def klass(cls):
self.assertIs(cls, Something)
@patch.object(Nothing, 'foo', 2)
@staticmethod
def static(arg):
return arg
@patch.dict(foo)
@classmethod
def klass_dict(cls):
self.assertIs(cls, Something)
@patch.dict(foo)
@staticmethod
def static_dict(arg):
return arg
# these will raise exceptions if patching descriptors is broken
self.assertEqual(Something.static('f00'), 'f00')
Something.klass()
self.assertEqual(Something.static_dict('f00'), 'f00')
Something.klass_dict()
something = Something()
self.assertEqual(something.static('f00'), 'f00')
something.klass()
self.assertEqual(something.static_dict('f00'), 'f00')
something.klass_dict()
def test_patch_spec_set(self):
@patch('%s.SomeClass' % __name__, spec=SomeClass, spec_set=True)
def test(MockClass):
......@@ -931,17 +868,13 @@ class PatchTest(unittest.TestCase):
def test_autospec(self):
class Boo(object):
def __init__(self, a):
pass
def f(self, a):
pass
def g(self):
pass
def __init__(self, a): pass
def f(self, a): pass
def g(self): pass
foo = 'bar'
class Bar(object):
def a(self):
pass
def a(self): pass
def _test(mock):
mock(1)
......@@ -1488,20 +1421,17 @@ class PatchTest(unittest.TestCase):
@patch.object(Foo, 'g', 1)
@patch.object(Foo, 'missing', 1)
@patch.object(Foo, 'f', 1)
def thing1():
pass
def thing1(): pass
@patch.object(Foo, 'missing', 1)
@patch.object(Foo, 'g', 1)
@patch.object(Foo, 'f', 1)
def thing2():
pass
def thing2(): pass
@patch.object(Foo, 'g', 1)
@patch.object(Foo, 'f', 1)
@patch.object(Foo, 'missing', 1)
def thing3():
pass
def thing3(): pass
for func in thing1, thing2, thing3:
self.assertRaises(AttributeError, func)
......@@ -1520,20 +1450,17 @@ class PatchTest(unittest.TestCase):
@patch.object(Foo, 'g', 1)
@patch.object(Foo, 'foo', new_callable=crasher)
@patch.object(Foo, 'f', 1)
def thing1():
pass
def thing1(): pass
@patch.object(Foo, 'foo', new_callable=crasher)
@patch.object(Foo, 'g', 1)
@patch.object(Foo, 'f', 1)
def thing2():
pass
def thing2(): pass
@patch.object(Foo, 'g', 1)
@patch.object(Foo, 'f', 1)
@patch.object(Foo, 'foo', new_callable=crasher)
def thing3():
pass
def thing3(): pass
for func in thing1, thing2, thing3:
self.assertRaises(NameError, func)
......@@ -1559,8 +1486,7 @@ class PatchTest(unittest.TestCase):
patcher.additional_patchers = additionals
@patcher
def func():
pass
def func(): pass
self.assertRaises(AttributeError, func)
self.assertEqual(Foo.f, original_f)
......@@ -1588,8 +1514,7 @@ class PatchTest(unittest.TestCase):
patcher.additional_patchers = additionals
@patcher
def func():
pass
def func(): pass
self.assertRaises(NameError, func)
self.assertEqual(Foo.f, original_f)
......@@ -1898,5 +1823,36 @@ class PatchTest(unittest.TestCase):
self.assertEqual(foo(), 1)
self.assertEqual(foo(), 0)
def test_dotted_but_module_not_loaded(self):
# This exercises the AttributeError branch of _dot_lookup.
# make sure it's there
import unittest.test.testmock.support
# now make sure it's not:
with patch.dict('sys.modules'):
del sys.modules['unittest.test.testmock.support']
del sys.modules['unittest.test.testmock']
del sys.modules['unittest.test']
del sys.modules['unittest']
# now make sure we can patch based on a dotted path:
@patch('unittest.test.testmock.support.X')
def test(mock):
pass
test()
def test_invalid_target(self):
with self.assertRaises(TypeError):
patch('')
def test_cant_set_kwargs_when_passing_a_mock(self):
@patch('unittest.test.testmock.support.X', new=object(), x=1)
def test(): pass
with self.assertRaises(TypeError):
test()
if __name__ == '__main__':
unittest.main()
......@@ -3,15 +3,10 @@ from unittest import mock
class SampleObject:
def __init__(self):
self.attr_sample1 = 1
self.attr_sample2 = 1
def method_sample1(self):
pass
def method_sample1(self): pass
def method_sample2(self):
pass
def method_sample2(self): pass
class TestSealable(unittest.TestCase):
......
......@@ -10,6 +10,8 @@ something = sentinel.Something
something_else = sentinel.SomethingElse
class SampleException(Exception): pass
class WithTest(unittest.TestCase):
......@@ -20,14 +22,10 @@ class WithTest(unittest.TestCase):
def test_with_statement_exception(self):
try:
with self.assertRaises(SampleException):
with patch('%s.something' % __name__, sentinel.Something2):
self.assertEqual(something, sentinel.Something2, "unpatched")
raise Exception('pow')
except Exception:
pass
else:
self.fail("patch swallowed exception")
raise SampleException()
self.assertEqual(something, sentinel.Something)
......@@ -128,8 +126,7 @@ class WithTest(unittest.TestCase):
def test_double_patch_instance_method(self):
class C:
def f(self):
pass
def f(self): pass
c = C()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment