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 and side_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.