[Python-checkins] cpython: Closes issue 14636. mock objects raise exceptions from an iterable side_effect
michael.foord
python-checkins at python.org
Sat Apr 21 16:52:18 CEST 2012
http://hg.python.org/cpython/rev/c310233b1d64
changeset: 76446:c310233b1d64
user: Michael Foord <michael at voidspace.org.uk>
date: Sat Apr 21 15:52:11 2012 +0100
summary:
Closes issue 14636. mock objects raise exceptions from an iterable side_effect
files:
Doc/library/unittest.mock-examples.rst | 49 --------------
Doc/library/unittest.mock.rst | 14 ++++
Lib/unittest/mock.py | 10 ++-
Lib/unittest/test/testmock/testmock.py | 10 ++
4 files changed, 31 insertions(+), 52 deletions(-)
diff --git a/Doc/library/unittest.mock-examples.rst b/Doc/library/unittest.mock-examples.rst
--- a/Doc/library/unittest.mock-examples.rst
+++ b/Doc/library/unittest.mock-examples.rst
@@ -838,56 +838,6 @@
children of a `CopyingMock` will also have the type `CopyingMock`.
-Multiple calls with different effects
--------------------------------------
-
-Handling code that needs to behave differently on subsequent calls during the
-test can be tricky. For example you may have a function that needs to raise
-an exception the first time it is called but returns a response on the second
-call (testing retry behaviour).
-
-One approach is to use a :attr:`side_effect` function that replaces itself. The
-first time it is called the `side_effect` sets a new `side_effect` that will
-be used for the second call. It then raises an exception:
-
- >>> def side_effect(*args):
- ... def second_call(*args):
- ... return 'response'
- ... mock.side_effect = second_call
- ... raise Exception('boom')
- ...
- >>> mock = Mock(side_effect=side_effect)
- >>> mock('first')
- Traceback (most recent call last):
- ...
- Exception: boom
- >>> mock('second')
- 'response'
- >>> mock.assert_called_with('second')
-
-Another perfectly valid way would be to pop return values from a list. If the
-return value is an exception, raise it instead of returning it:
-
- >>> returns = [Exception('boom'), 'response']
- >>> def side_effect(*args):
- ... result = returns.pop(0)
- ... if isinstance(result, Exception):
- ... raise result
- ... return result
- ...
- >>> mock = Mock(side_effect=side_effect)
- >>> mock('first')
- Traceback (most recent call last):
- ...
- Exception: boom
- >>> mock('second')
- 'response'
- >>> mock.assert_called_with('second')
-
-Which approach you prefer is a matter of taste. The first approach is actually
-a line shorter but maybe the second approach is more readable.
-
-
Nesting Patches
---------------
diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst
--- a/Doc/library/unittest.mock.rst
+++ b/Doc/library/unittest.mock.rst
@@ -823,6 +823,20 @@
...
StopIteration
+If any members of the iterable are exceptions they will be raised instead of
+returned::
+
+ >>> iterable = (33, ValueError, 66)
+ >>> m = MagicMock(side_effect=iterable)
+ >>> m()
+ 33
+ >>> m()
+ Traceback (most recent call last):
+ ...
+ ValueError
+ >>> m()
+ 66
+
.. _deleting-attributes:
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -891,7 +891,10 @@
raise effect
if not _callable(effect):
- return next(effect)
+ result = next(effect)
+ if _is_exception(result):
+ raise result
+ return result
ret_val = effect(*args, **kwargs)
if ret_val is DEFAULT:
@@ -931,8 +934,9 @@
arguments as the mock, and unless it returns `DEFAULT`, the return
value of this function is used as the return value.
- Alternatively `side_effect` can be an exception class or instance. In
- this case the exception will be raised when the mock is called.
+ If `side_effect` is an iterable then each call to the mock will return
+ the next value from the iterable. If any of the members of the iterable
+ are exceptions they will be raised instead of returned.
If `side_effect` is an iterable then each call to the mock will return
the next value from the iterable.
diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py
--- a/Lib/unittest/test/testmock/testmock.py
+++ b/Lib/unittest/test/testmock/testmock.py
@@ -868,6 +868,16 @@
self.assertRaises(StopIteration, mock)
+ def test_side_effect_iterator_exceptions(self):
+ for Klass in Mock, MagicMock:
+ iterable = (ValueError, 3, KeyError, 6)
+ m = Klass(side_effect=iterable)
+ self.assertRaises(ValueError, m)
+ self.assertEqual(m(), 3)
+ self.assertRaises(KeyError, m)
+ self.assertEqual(m(), 6)
+
+
def test_side_effect_setting_iterator(self):
mock = Mock()
mock.side_effect = iter([1, 2, 3])
--
Repository URL: http://hg.python.org/cpython
More information about the Python-checkins
mailing list