Mock's return_value & side effect
Mock is an object that simulates the behavior of other object. In python we have a builtin mock module in unittest
library.
from unittest import mock
>>> m = mock.Mock()
# You can try to read some non-existing attribute - it will just return mock.
>>> m.some_attribute
<Mock name='mock.some_attribute' id='140126083485648'>
# You can also try to call a method - it will return another mock too.
>>> m.process_data()
<Mock name='mock.process_data()' id='140126083843264'>
Mock have a return_value
attribute to help you simulate specified behavior in tests. It allows to simply return a value.
>>> m = mock.Mock()
>>> m.process_data.return_value = 'The answer is 42'
>>> m.process_data()
'The answer is 42'
>>> m.process_data()
'The answer is 42'
You can assign anything - integers, string, tuples, dicts, classes, class instances, etc...
Side effect - a multifunctional tool
Mock's side_effect
parameter allows to change the behavior of the mock. It accepts three types of values and changes its behavior accordingly.
side_effect = Exception
The mock will rise passed exception
>>> m.simulate_fail.side_effect = ValueError('Whoops...')
>>> m.simulate_fail()
Traceback (most recent call last):
[...]
File ".../lib/python3.8/unittest/mock.py", line 1140, in _execute_mock_call
raise effect
ValueError: Whoops...
side_effect = Iterable
The mock will yield the values from this iterable on subsequent call
>>> m.my_attribute.side_effect = [5, 10, 42, ValueError('Something happened')]
>>> m.my_attribute()
5
>>> m.my_attribute()
10
>>> m.my_attribute()
42
>>> m.my_attribute()
Traceback (most recent call last):
[...]
File ".../lib/python3.8/unittest/mock.py", line 1140, in _execute_mock_call
raise effect
ValueError: Something happened
side_effect = callable
The callable will be executed on each call with the parameters passed when calling the mocked method. Any callable will do, so it can be a function, or a class
# Class-based example
class Person:
def __init__(self, name):
self.name = name
>>> m.my_attribute.side_effect = Person
>>> friend = m.my_attribute('Max')
>>> friend.name
'Max'
>>> repr(friend)
'<__main__.Person object at 0x7f71940ad2e0>'
# Function-based example
def log_calls(*args, **kwargs):
print(f'Called with {args} and {kwargs}')
>>> m.another_attribute.side_effect = log_calls
>>> m.another_attribute()
Called with () and {}
>>> m.another_attribute(42, name='Peter', mood='Good')
Called with (42,) and {'name': 'Peter', 'mood': 'Good'}
Notes
pytest-mock - is a thin-wrapper around the mock package for easier use with pytest.