[issue35330] When using mock to wrap an existing object, side_effect requires return_value

Karthikeyan Singaravelan report at bugs.python.org
Wed Nov 28 07:32:08 EST 2018


Karthikeyan Singaravelan <tir.karthi at gmail.com> added the comment:

I did some debugging with docstring for wraps.


> `wraps`: Item for the mock object to wrap. If `wraps` is not None then
>  calling the Mock will pass the call through to the wrapped object
>  (returning the real result). Attribute access on the mock will return a
>  Mock object that wraps the corresponding attribute of the wrapped object
>  (so attempting to access an attribute that doesn't exist will raise an
>  `AttributeError`).
>  If the mock has an explicit return_value set then calls are not passed to the wrapped object and the return_value is returned instead.

So calling mock.Mock(wraps=c) sets the _mock_wraps with c. When we set m.func.side_effect and call m.func() it checks for the side_effect (func2) to be a callable and calls it [0]. It also checks if self._mock_wraps is not None which in this case is MyClass() and checks for the func of Myclass that is also called at [1] . As per the docstring since it wraps the actual object calling m.invalid_func without return_value set will cause attribute error like "AttributeError: 'MyClass' object has no attribute 'invalid_func'"

It seems to be a general case with mock itself where when side_effect and return_value are set then side_effect is called and ignores the return_value set unless the side_effect returns the sentinel value DEFAULT as in test [2]. I find this to be surprising.

$ ./python.exe
Python 3.8.0a0 (heads/master:b7278736b3, Nov 28 2018, 10:26:47)
[Clang 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from unittest import mock
>>> m = mock.Mock()
>>> m.side_effect = lambda : 2
>>> m.return_value = 3
>>> m() # side_effect and return_value are set returning side_effect
2
>>> f = mock.Mock()
>>> f.return_value = 3
>>> f() # side_effect is not set thus returns only return_value
3

As per the original report when there is a return_value set without the side_effect it returns the set return_value. When there is a return_value set with the side_effect then return value of the side effect is returned though return_value is explicitly set like above also with wraps

>>> m = mock.Mock(wraps=c)
>>> print(m.func())
func called
1
>>> m.func.return_value = 3
>>> print(m.func())
3
>>> f = mock.Mock(wraps=c)
>>> f.func.side_effect = func2
>>> f.func.return_value = 3
>>> print(f.func())
func2 called
2

[0] https://github.com/python/cpython/blob/bde9d6bbb46ca59bcee5d5060adaa33c3ffee3a6/Lib/unittest/mock.py#L1031
[1] https://github.com/python/cpython/blob/bde9d6bbb46ca59bcee5d5060adaa33c3ffee3a6/Lib/unittest/mock.py#L1041
[2] https://github.com/python/cpython/blob/54ba556c6c7d8fd5504dc142c2e773890c55a774/Lib/unittest/test/testmock/testmock.py#L156

----------
nosy: +mariocj89, michael.foord

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue35330>
_______________________________________


More information about the Python-bugs-list mailing list