Python 3 mock objects support being queried for arguments to their calls, is it possible to also query them for the values returned by their calls?
My particular scenario is that I mock tempfile.mkdtemp, but as a side effect call the real mkdtemp. I'd like to get hold of the created temporary directory in my test.
from unittest import mock
import shutil
import tempfile
from app import production_function
def mkdtemp(*args, **kwargs):
dtemp = orig_mkdtemp(*args, **kwargs)
return dtemp
orig_mkdtemp = tempfile.mkdtemp
patcher = mock.patch('tempfile.mkdtemp', name='tempfile.mkdtemp')
the_mock = patcher.start()
the_mock.side_effect = mkdtemp
# Call function under test
production_function()
assert the_mock.called
# Now, how to get the return value from the call to the_mock?
patcher.stop()
Unfortunately mock
module don't store return value (I took a look by debugguer and there isn't any trace of it). You must store it before return the value of side_effect
.
You can use an object to take care of the dirty work. For instance a very base implementation can be something like that:
class SideEffect():
def __init__(self, n):
self.values = iter(range(n))
self.return_value = None
def __call__(self):
self.return_value = next(self.values)
return self.return_value
a = Mock()
se = SideEffect(10)
a.side_effect = se
for x in range(10):
v = a()
assert v == se.return_value
print("a()={} return_value={}".format(v, se.return_value))
If you want a more sophisticated side_effect
that wrap a function and take care of arguments and exception an example could be:
class GenericSideEffect():
def __init__(self, f, *args, **kwargs):
self.v_function = f
self.args = args
self.kwargs = kwargs
self._return_value = Exception("Never Called")
def __call__(self):
try:
self._return_value = self.v_function(*self.args, **self.kwargs)
return self._return_value
except Exception as e:
self.return_value = e
raise e
@property
def return_value(self):
if isinstance(self._return_value, Exception):
raise self._return_value
return self._return_value
Of course you can write it as decorator and preserve the signature, but I think that part is out of scope in that answer.