Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2cd4873

Browse files
committed
Closes issue 14636. mock objects raise exceptions from an iterable side_effect
1 parent 24117a7 commit 2cd4873

4 files changed

Lines changed: 31 additions & 53 deletions

File tree

Doc/library/unittest.mock-examples.rst

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -838,56 +838,6 @@ and the `return_value` will use your subclass automatically. That means all
838838
children of a `CopyingMock` will also have the type `CopyingMock`.
839839

840840

841-
Multiple calls with different effects
842-
-------------------------------------
843-
844-
Handling code that needs to behave differently on subsequent calls during the
845-
test can be tricky. For example you may have a function that needs to raise
846-
an exception the first time it is called but returns a response on the second
847-
call (testing retry behaviour).
848-
849-
One approach is to use a :attr:`side_effect` function that replaces itself. The
850-
first time it is called the `side_effect` sets a new `side_effect` that will
851-
be used for the second call. It then raises an exception:
852-
853-
>>> def side_effect(*args):
854-
... def second_call(*args):
855-
... return 'response'
856-
... mock.side_effect = second_call
857-
... raise Exception('boom')
858-
...
859-
>>> mock = Mock(side_effect=side_effect)
860-
>>> mock('first')
861-
Traceback (most recent call last):
862-
...
863-
Exception: boom
864-
>>> mock('second')
865-
'response'
866-
>>> mock.assert_called_with('second')
867-
868-
Another perfectly valid way would be to pop return values from a list. If the
869-
return value is an exception, raise it instead of returning it:
870-
871-
>>> returns = [Exception('boom'), 'response']
872-
>>> def side_effect(*args):
873-
... result = returns.pop(0)
874-
... if isinstance(result, Exception):
875-
... raise result
876-
... return result
877-
...
878-
>>> mock = Mock(side_effect=side_effect)
879-
>>> mock('first')
880-
Traceback (most recent call last):
881-
...
882-
Exception: boom
883-
>>> mock('second')
884-
'response'
885-
>>> mock.assert_called_with('second')
886-
887-
Which approach you prefer is a matter of taste. The first approach is actually
888-
a line shorter but maybe the second approach is more readable.
889-
890-
891841
Nesting Patches
892842
---------------
893843

Doc/library/unittest.mock.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,20 @@ a `StopIteration` is raised):
823823
...
824824
StopIteration
825825

826+
If any members of the iterable are exceptions they will be raised instead of
827+
returned::
828+
829+
>>> iterable = (33, ValueError, 66)
830+
>>> m = MagicMock(side_effect=iterable)
831+
>>> m()
832+
33
833+
>>> m()
834+
Traceback (most recent call last):
835+
...
836+
ValueError
837+
>>> m()
838+
66
839+
826840

827841
.. _deleting-attributes:
828842

Lib/unittest/mock.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,10 @@ def _mock_call(_mock_self, *args, **kwargs):
891891
raise effect
892892

893893
if not _callable(effect):
894-
return next(effect)
894+
result = next(effect)
895+
if _is_exception(result):
896+
raise result
897+
return result
895898

896899
ret_val = effect(*args, **kwargs)
897900
if ret_val is DEFAULT:
@@ -931,8 +934,9 @@ class or instance) that acts as the specification for the mock object. If
931934
arguments as the mock, and unless it returns `DEFAULT`, the return
932935
value of this function is used as the return value.
933936
934-
Alternatively `side_effect` can be an exception class or instance. In
935-
this case the exception will be raised when the mock is called.
937+
If `side_effect` is an iterable then each call to the mock will return
938+
the next value from the iterable. If any of the members of the iterable
939+
are exceptions they will be raised instead of returned.
936940
937941
If `side_effect` is an iterable then each call to the mock will return
938942
the next value from the iterable.

Lib/unittest/test/testmock/testmock.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,16 @@ class Foo(object):
868868
self.assertRaises(StopIteration, mock)
869869

870870

871+
def test_side_effect_iterator_exceptions(self):
872+
for Klass in Mock, MagicMock:
873+
iterable = (ValueError, 3, KeyError, 6)
874+
m = Klass(side_effect=iterable)
875+
self.assertRaises(ValueError, m)
876+
self.assertEqual(m(), 3)
877+
self.assertRaises(KeyError, m)
878+
self.assertEqual(m(), 6)
879+
880+
871881
def test_side_effect_setting_iterator(self):
872882
mock = Mock()
873883
mock.side_effect = iter([1, 2, 3])

0 commit comments

Comments
 (0)