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
just use pytest-antilru.
pip install pytest-antilru
and you are good to go
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With