Using pytest monkeypatch to a mock function

Pytest is a widely used testing library. A feature that has helped me every now and then is monkeypatch.

Here are a couple of examples mocking a function output.

For example, lets say that we want to test a function in the code which retrieves data from an external api and process it. To speed up the testing environment and reduce the flakiness we can mock the reponse from the external service. This can be done with:

import external_service.requests
from mock_constants import EXTENAL_PRICES
from important_functions import function_to_test


def test_function_a(monkeypatch):
    monkeypatch.setattr(
        external_service.requests, 
        "get_prices", 
        lambda *args, **kwargs: EXTENAL_PRICES
    )
    processed_data = function_to_test()
    assert processed_data.attr_a == EXTENAL_PRICES.attr_a
    ...

Note that the third argument of monkeypatch.setattr is a function. On the example above the lambda accepts any arguments, ignores them and returns a fix value. However, the mocked function could process the inputs.

Mocked function can process arguments

Note that the mocked function argument names must match those from the real function

import external_service.requests
from mock_constants import EXTENAL_PRICES, INTERNAL_PRICES
from important_functions import function_to_test


def test_function_a(monkeypatch):
    def mocked_fn(arg1):
        # Note that the argument names must match 
        # those from the real function
        if arg1 is True:
            return EXTENAL_PRICES
        else:
            return INTERNAL_PRICES

    monkeypatch.setattr(
        external_service.requests, 
        "get_prices", 
        mocked_fn
    )
    processed_data = function_to_test()
    assert processed_data.attr_a == EXTENAL_PRICES.attr_a
    assert processed_data.attr_a != INTERNAL_PRICES.attr_a
    ...

This can be useful for creating paramatrized tests.