Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python: clear / reset `@lru_cache` functool caching with every pytest testcase for undisturbed mocking

I combine mocking and caching in my code. The mocking is (kind of) random for each pytest as I do not know exactly, what will be returned in the real case. Hence I want to mock the very same function (in my example fct_child) with different values in different testcases. The caching makes some problems however, as the return values (in my example of fct_parent are cached and hence the mocked function is only reached in the first testcase and then always skipped due to the caching of the parent function. I need to find a way to clear/reset the cache between pytests.

In the following code the tests test_1 and test_2 can be executed independently of each over ($ pytest test_main.py::test_1 and $ pytest test_main.py::test_2), successfully. If pytest runs over the full module ($ pytest test_main.py), however, the second test crashes. Also the main part works ($ python test_main.py), where I ensure, that caching works as expected.

So how can I fix the code, such that pytest also passes when all testcases are executed (the $ pytest test_main.py scenario)?

test_main.py

# test_main.py

from my_lib import fct_parent, get_n_calls_fct_child

class ChildMock:

    def __init__(self, val_child):
        self.n_calls_mock = 0
        self.val_child = val_child

    def fct(self):
        self.n_calls_mock += 1
        return self.val_child

def test_1(monkeypatch):
    """This test interacts with test_2:
    Exectuing each test independently with pytest works, executing both in one run, fails.
    This is due to the lru_cache being not cleaned.
    """
    val_child = "mocked test 1"
    child_mock = ChildMock(val_child)

    with monkeypatch.context() as mpc:
        mpc.setattr("my_lib.fct_child", child_mock.fct)  # mocks fct_child to return ret_val
        assert fct_parent() == val_child
        assert fct_parent() == val_child
        assert child_mock.n_calls_mock == 1

def test_2(monkeypatch):
    """This test interacts with test_1:
    Exectuing each test independently with pytest works, executing both in one run, fails.
    This is due to the lru_cache being not cleaned.
    """
    val_child = "mocked test 2"
    child_mock = ChildMock(val_child)

    with monkeypatch.context() as mpc:
        mpc.setattr("my_lib.fct_child", child_mock.fct)  # mocks fct_child to return ret_val
        assert fct_parent() == val_child
        assert fct_parent() == val_child
        assert child_mock.n_calls_mock == 1

if __name__ == "__main__":
    assert fct_parent() == "unmocked"
    assert fct_parent() == "unmocked"
    n_calls_fct_child = get_n_calls_fct_child()
    assert n_calls_fct_child == 1, f"{n_calls_fct_child=} should be == 1"
    print("good: fct_child was only computed once")

my_lib.py

# my_lib.py

from functools import lru_cache

_n_child_calls = 0

@lru_cache(256)
def fct_parent():
    return fct_child()

def fct_child():
    global _n_child_calls
    _n_child_calls += 1
    return "unmocked"

def get_n_calls_fct_child():
    return _n_child_calls
like image 625
Markus Dutschke Avatar asked Nov 04 '25 10:11

Markus Dutschke


1 Answers

just use pytest-antilru.

pip install pytest-antilru

and you are good to go

like image 186
IlanJB Avatar answered Nov 07 '25 05:11

IlanJB



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!