Role of side effect
in Mocking
A side_effect
mock function is a way to simulate external interactions, exceptions, or dynamic behavior during the execution of a mocked function.
Let’s just say that it allows you to go beyond a simple return value and introduce more complex effects.
It’s like telling the function, “Hey, when you’re called, in addition to returning a value, also do something special.”
import pytestdef test_mock_with_side_effect(mocker): m = mocker.Mock() m.side_effect = ['Pytest', 'with', 'Eric'] assert m() == 'Pytest' assert m() == 'with' assert m() == 'Eric'
Difference between side_effect
and return_value
return_value
in a mock sets the value that the mocked function will return. Pretty straightforward.
On the other hand, side_effect
allows you to define a function or an exception to be raised when the mock is called. It provides more flexibility, enabling dynamic behavior based on the input or other conditions.
Multiple Return Values with ‘side_effect’
Instead of being confined to returning a single value, side_effect
allows you to define a function or iterable that produces a sequence of return values.
side_effect
also allows you to define exceptions to be raised when the mock is called.
If the side_effect
is an iterable (e.g. a list or tuple), the mocked object will keep returning values from the iterable depending on whether the iterable is exhausted or not.
If the iterable is not exhausted, the mocked object will return values from the beginning of the iterable in a cyclic manner.
If the iterable is exhausted (all elements are used), further calls to the mocked object will raise a StopIteration
exception.
The solution to avoid exhaustion is to implement fallback behaviour. For example, you can raise a custom exception or return a default value.
Best Practices and Tips
Let’s unwrap some tips and tricks you need to follow while using mocking to return multiple values.
-
Mocks should be clear, concise, and easy to understand. Avoid unnecessary complexity in mock setups.
-
Use the
return_value
attribute to set the return value of a mock object andside_effect
to define a function or iterable that produces a sequence of return values or exceptions to be raised when the mock is called. -
Too much mocking can be very hard to track and lead to unpredictable behaviour. Some good practices include using a fixture for mocking and controlling its life with the scope.
-
One way to achieve a clean testing environment is to use Pytest Fixtures to manage Setup and Teardown.
-
Using fixtures for mocking allows better control over the lifecycle of mocks. By controlling the scope, you ensure that mocks are appropriately isolated and don’t interfere with other parts of your test suite.
-
Also, it’s important to handle exceptions for testing error handling, validating exception propagation, or ensuring that exceptions do not break your system.