summaryrefslogtreecommitdiffstats
path: root/Doc
diff options
context:
space:
mode:
authorinfohash <46137868+infohash@users.noreply.github.com>2024-03-22 09:47:52 (GMT)
committerGitHub <noreply@github.com>2024-03-22 09:47:52 (GMT)
commitad0ff8603432d08fcd9521799e6a38ba941b7e60 (patch)
treeb7bf5086ae33b193830244711805a93f6f012fe0 /Doc
parentd3de3a251efe213155b8996effe235408536d0e0 (diff)
downloadcpython-ad0ff8603432d08fcd9521799e6a38ba941b7e60.zip
cpython-ad0ff8603432d08fcd9521799e6a38ba941b7e60.tar.gz
cpython-ad0ff8603432d08fcd9521799e6a38ba941b7e60.tar.bz2
[3.12] gh-75988: Fix issues with autospec ignoring wrapped object (GH-115223) (#117119)
gh-75988: Fix issues with autospec ignoring wrapped object (#115223) * set default return value of functional types as _mock_return_value * added test of wrapping child attributes * added backward compatibility with explicit return * added docs on the order of precedence * added test to check default return_value (cherry picked from commit 735fc2cbbcf875c359021b5b2af7f4c29f4cf66d)
Diffstat (limited to 'Doc')
-rw-r--r--Doc/library/unittest.mock.rst120
1 files changed, 120 insertions, 0 deletions
diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst
index 478c4e2..108b5ef 100644
--- a/Doc/library/unittest.mock.rst
+++ b/Doc/library/unittest.mock.rst
@@ -2782,3 +2782,123 @@ Sealing mocks
>>> mock.not_submock.attribute2 # This won't raise.
.. versionadded:: 3.7
+
+
+Order of precedence of :attr:`side_effect`, :attr:`return_value` and *wraps*
+----------------------------------------------------------------------------
+
+The order of their precedence is:
+
+1. :attr:`~Mock.side_effect`
+2. :attr:`~Mock.return_value`
+3. *wraps*
+
+If all three are set, mock will return the value from :attr:`~Mock.side_effect`,
+ignoring :attr:`~Mock.return_value` and the wrapped object altogether. If any
+two are set, the one with the higher precedence will return the value.
+Regardless of the order of which was set first, the order of precedence
+remains unchanged.
+
+ >>> from unittest.mock import Mock
+ >>> class Order:
+ ... @staticmethod
+ ... def get_value():
+ ... return "third"
+ ...
+ >>> order_mock = Mock(spec=Order, wraps=Order)
+ >>> order_mock.get_value.side_effect = ["first"]
+ >>> order_mock.get_value.return_value = "second"
+ >>> order_mock.get_value()
+ 'first'
+
+As ``None`` is the default value of :attr:`~Mock.side_effect`, if you reassign
+its value back to ``None``, the order of precedence will be checked between
+:attr:`~Mock.return_value` and the wrapped object, ignoring
+:attr:`~Mock.side_effect`.
+
+ >>> order_mock.get_value.side_effect = None
+ >>> order_mock.get_value()
+ 'second'
+
+If the value being returned by :attr:`~Mock.side_effect` is :data:`DEFAULT`,
+it is ignored and the order of precedence moves to the successor to obtain the
+value to return.
+
+ >>> from unittest.mock import DEFAULT
+ >>> order_mock.get_value.side_effect = [DEFAULT]
+ >>> order_mock.get_value()
+ 'second'
+
+When :class:`Mock` wraps an object, the default value of
+:attr:`~Mock.return_value` will be :data:`DEFAULT`.
+
+ >>> order_mock = Mock(spec=Order, wraps=Order)
+ >>> order_mock.return_value
+ sentinel.DEFAULT
+ >>> order_mock.get_value.return_value
+ sentinel.DEFAULT
+
+The order of precedence will ignore this value and it will move to the last
+successor which is the wrapped object.
+
+As the real call is being made to the wrapped object, creating an instance of
+this mock will return the real instance of the class. The positional arguments,
+if any, required by the wrapped object must be passed.
+
+ >>> order_mock_instance = order_mock()
+ >>> isinstance(order_mock_instance, Order)
+ True
+ >>> order_mock_instance.get_value()
+ 'third'
+
+ >>> order_mock.get_value.return_value = DEFAULT
+ >>> order_mock.get_value()
+ 'third'
+
+ >>> order_mock.get_value.return_value = "second"
+ >>> order_mock.get_value()
+ 'second'
+
+But if you assign ``None`` to it, this will not be ignored as it is an
+explicit assignment. So, the order of precedence will not move to the wrapped
+object.
+
+ >>> order_mock.get_value.return_value = None
+ >>> order_mock.get_value() is None
+ True
+
+Even if you set all three at once when initializing the mock, the order of
+precedence remains the same:
+
+ >>> order_mock = Mock(spec=Order, wraps=Order,
+ ... **{"get_value.side_effect": ["first"],
+ ... "get_value.return_value": "second"}
+ ... )
+ ...
+ >>> order_mock.get_value()
+ 'first'
+ >>> order_mock.get_value.side_effect = None
+ >>> order_mock.get_value()
+ 'second'
+ >>> order_mock.get_value.return_value = DEFAULT
+ >>> order_mock.get_value()
+ 'third'
+
+If :attr:`~Mock.side_effect` is exhausted, the order of precedence will not
+cause a value to be obtained from the successors. Instead, ``StopIteration``
+exception is raised.
+
+ >>> order_mock = Mock(spec=Order, wraps=Order)
+ >>> order_mock.get_value.side_effect = ["first side effect value",
+ ... "another side effect value"]
+ >>> order_mock.get_value.return_value = "second"
+
+ >>> order_mock.get_value()
+ 'first side effect value'
+ >>> order_mock.get_value()
+ 'another side effect value'
+
+ >>> order_mock.get_value()
+ Traceback (most recent call last):
+ ...
+ StopIteration