diff options
author | Mario Corchero <mariocj89@gmail.com> | 2018-12-08 11:25:02 +0000 |
---|---|---|
committer | Chris Withers <chris@withers.org> | 2019-04-30 08:39:55 +0100 |
commit | 796edd45b3eab4dc8b9fe17c528bc51f6a613b67 (patch) | |
tree | 917f3770199bc855cb0efdfc16f5f695889029a0 /mock | |
parent | b1c060e558639a95f98b13244367da9f96810aa9 (diff) | |
download | mock-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.py | 21 | ||||
-rw-r--r-- | mock/tests/testmock.py | 122 |
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) |