New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
When using mock to wrap an existing object, side_effect requires return_value #79511
Comments
When using mock to wrap an existing object, and using side_effect to set a function to wrap a method, I would expect the wrapper function to be called instead of the wrapped function, and its return value to be returned. Instead, both the wrapper function and the wrapped functions are being called, and the return value of the wrapped function is returned. If, in addition to side_effect, return_value is set, the return_value is ignored, but my expected behavior actually happens: only the wrapper function is called, and its return value is returned. Python 3.7.0 (default, Aug 22 2018, 20:50:05)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from unittest import mock
>>> class MyClass(object):
... def func(self):
... print('func called')
... return 1
...
>>> c = MyClass()
>>> m = mock.Mock(wraps=c)
>>> def func2():
... print('func2 called')
... return 2
...
>>> m.func.side_effect = func2
>>> m.func()
func2 called
func called
1
>>> m.func.return_value = 3
>>> m.func()
func2 called
2 |
I did some debugging with docstring for wraps.
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] Line 1031 in bde9d6b
[1] Line 1041 in bde9d6b
[2]
|
A little more discussion over side_effect and return_value precedence : bpo-22541 |
I can indeed reproduce the issue. The problem seems to be here: Line 1041 in 54ba556
The simplified current logic in that code is:
That explains why you see your "expected behavior" to happen only when return value is set. Basically, the logic disables the current wrapped object ONLY if return value is set. One might claim the code is there because I'd suggest a patch adding the The behavior of This bug can be reproduced without a class at all, see:
Results in:
Whilst the expected is:
## TL;DR; Indeed, Noam Yorav-Raphael I am happy to send the patch if you don't have time :) |
Thanks @mariocj89 for the explanation. I just got to the docs part about side_effect and return_value precedence. I am curious to know about the behavior as well and at least this can be added as a test as I see only around 3 tests for side_effect and there is no test for wraps behavior in this report I assume given that there were no test failures due to changing the logic. |
I'll get ready a PR with a good set of tests and the fix for the original issue. This is quite an interesting bug :) |
side_effect
is set #10973side_effect
is set (GH10973) #11034side_effect
is set (GH10973) #11035Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: