summaryrefslogtreecommitdiff
path: root/mock
diff options
context:
space:
mode:
authorMario Corchero <mariocj89@gmail.com>2018-12-08 11:25:02 +0000
committerChris Withers <chris@withers.org>2019-04-30 08:39:55 +0100
commit796edd45b3eab4dc8b9fe17c528bc51f6a613b67 (patch)
tree917f3770199bc855cb0efdfc16f5f695889029a0 /mock
parentb1c060e558639a95f98b13244367da9f96810aa9 (diff)
downloadmock-796edd45b3eab4dc8b9fe17c528bc51f6a613b67.tar.gz
bpo-35330: Don't call the wrapped object if `side_effect` is set (GH10973)
* tests: Further validate `wraps` functionality in `unittest.mock.Mock` Add more tests to validate how `wraps` interacts with other features of mocks. * Don't call the wrapped object if `side_effect` is set When a object is wrapped using `Mock(wraps=...)`, if an user sets a `side_effect` in one of their methods, return the value of `side_effect` and don't call the original object. * Refactor what to be called on `mock_call` When a `Mock` is called, it should return looking up in the following order: `side_effect`, `return_value`, `wraps`. If any of the first two return `mock.DEFAULT`, lookup in the next option. It makes no sense to check for `wraps` returning default, as it is supposed to be the original implementation and there is nothing to fallback to. Backports: f05df0a4b679d0acfd0b1fe6187ba2d553b37afa Signed-off-by: Chris Withers <chris@withers.org>
Diffstat (limited to 'mock')
-rw-r--r--mock/mock.py21
-rw-r--r--mock/tests/testmock.py122
2 files changed, 132 insertions, 11 deletions
diff --git a/mock/mock.py b/mock/mock.py
index 9160268..2724fae 100644
--- a/mock/mock.py
+++ b/mock/mock.py
@@ -1165,28 +1165,27 @@ class CallableMixin(Base):
break
seen.add(_new_parent_id)
- ret_val = DEFAULT
effect = self.side_effect
if effect is not None:
if _is_exception(effect):
raise effect
-
- if not _callable(effect):
+ elif not _callable(effect):
result = next(effect)
if _is_exception(result):
raise result
- if result is DEFAULT:
- result = self.return_value
+ else:
+ result = effect(*args, **kwargs)
+
+ if result is not DEFAULT:
return result
- ret_val = effect(*args, **kwargs)
+ if self._mock_return_value is not DEFAULT:
+ return self.return_value
- if (self._mock_wraps is not None and
- self._mock_return_value is DEFAULT):
+ if self._mock_wraps is not None:
return self._mock_wraps(*args, **kwargs)
- if ret_val is DEFAULT:
- ret_val = self.return_value
- return ret_val
+
+ return self.return_value
diff --git a/mock/tests/testmock.py b/mock/tests/testmock.py
index 43e23b2..8a8e7c3 100644
--- a/mock/tests/testmock.py
+++ b/mock/tests/testmock.py
@@ -583,6 +583,16 @@ class MockTest(unittest.TestCase):
real.assert_called_with(1, 2, fish=3)
+ def test_wraps_prevents_automatic_creation_of_mocks(self):
+ class Real(object):
+ pass
+
+ real = Real()
+ mock = Mock(wraps=real)
+
+ self.assertRaises(AttributeError, lambda: mock.new_attr())
+
+
def test_wraps_call_with_nondefault_return_value(self):
real = Mock()
@@ -609,6 +619,118 @@ class MockTest(unittest.TestCase):
self.assertEqual(result, Real.attribute.frog())
+ def test_customize_wrapped_object_with_side_effect_iterable_with_default(self):
+ class Real(object):
+ def method(self):
+ return sentinel.ORIGINAL_VALUE
+
+ real = Real()
+ mock = Mock(wraps=real)
+ mock.method.side_effect = [sentinel.VALUE1, DEFAULT]
+
+ self.assertEqual(mock.method(), sentinel.VALUE1)
+ self.assertEqual(mock.method(), sentinel.ORIGINAL_VALUE)
+ self.assertRaises(StopIteration, mock.method)
+
+
+ def test_customize_wrapped_object_with_side_effect_iterable(self):
+ class Real(object):
+ def method(self):
+ raise NotImplementedError()
+
+ real = Real()
+ mock = Mock(wraps=real)
+ mock.method.side_effect = [sentinel.VALUE1, sentinel.VALUE2]
+
+ self.assertEqual(mock.method(), sentinel.VALUE1)
+ self.assertEqual(mock.method(), sentinel.VALUE2)
+ self.assertRaises(StopIteration, mock.method)
+
+
+ def test_customize_wrapped_object_with_side_effect_exception(self):
+ class Real(object):
+ def method(self):
+ raise NotImplementedError()
+
+ real = Real()
+ mock = Mock(wraps=real)
+ mock.method.side_effect = RuntimeError
+
+ self.assertRaises(RuntimeError, mock.method)
+
+
+ def test_customize_wrapped_object_with_side_effect_function(self):
+ class Real(object):
+ def method(self):
+ raise NotImplementedError()
+
+ def side_effect():
+ return sentinel.VALUE
+
+ real = Real()
+ mock = Mock(wraps=real)
+ mock.method.side_effect = side_effect
+
+ self.assertEqual(mock.method(), sentinel.VALUE)
+
+
+ def test_customize_wrapped_object_with_return_value(self):
+ class Real(object):
+ def method(self):
+ raise NotImplementedError()
+
+ real = Real()
+ mock = Mock(wraps=real)
+ mock.method.return_value = sentinel.VALUE
+
+ self.assertEqual(mock.method(), sentinel.VALUE)
+
+
+ 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()
+
+ real = Real()
+ mock = Mock(wraps=real)
+ mock.method.side_effect = [sentinel.VALUE1, sentinel.VALUE2]
+ mock.method.return_value = sentinel.WRONG_VALUE
+
+ self.assertEqual(mock.method(), sentinel.VALUE1)
+ self.assertEqual(mock.method(), sentinel.VALUE2)
+ self.assertRaises(StopIteration, mock.method)
+
+
+ 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()
+
+ real = Real()
+ mock = Mock(wraps=real)
+ mock.method.side_effect = lambda: DEFAULT
+ mock.method.return_value = sentinel.VALUE
+
+ self.assertEqual(mock.method(), sentinel.VALUE)
+
+
+ def test_customize_wrapped_object_with_return_value_and_side_effect_default(self):
+ class Real(object):
+ def method(self):
+ raise NotImplementedError()
+
+ real = Real()
+ mock = Mock(wraps=real)
+ mock.method.side_effect = [sentinel.VALUE1, DEFAULT]
+ mock.method.return_value = sentinel.RETURN
+
+ self.assertEqual(mock.method(), sentinel.VALUE1)
+ self.assertEqual(mock.method(), sentinel.RETURN)
+ self.assertRaises(StopIteration, mock.method)
+
+
def test_exceptional_side_effect(self):
mock = Mock(side_effect=AttributeError)
self.assertRaises(AttributeError, mock)