我在代码中结合了模拟和缓存。mocking对于每个pytest来说都是(某种程度上)随机的,我不知道,在实际情况下将返回什么。因此,我想在不同的测试用例中使用不同的值来模拟相同的函数(在我的示例fct_child中)。但是,缓存会产生一些问题,因为返回值(在我的fct_parent示例中是缓存的,因此模拟的函数只在第一个测试用例中到达,然后由于父函数的缓存而始终跳过)。我需要找到一种方法来清除/重置pytest之间的缓存。
在下面的代码中,可以成功地独立地执行测试test_1和test_2 ($ pytest test_main.py::test_1和$ pytest test_main.py::test_2)。但是,如果pytest运行在完整模块($ pytest test_main.py)上,那么第二个测试就会崩溃。此外,主体部分工作($ python test_main.py),我确保缓存按预期工作。
那么,如何修复代码,使pytest在执行所有测试用例时也通过( $ pytest test_main.py场景)?
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发布于 2022-05-12 21:37:10
下面的方法定义了一个@decorator,一旦到达一个新的testcase,clear就是修饰函数的cache。
my_lib_fixed.py
import os
from functools import lru_cache, wraps
_pytest_cache_func = {} # Dict {'func.__name__: name_of_pytest_with_last_caching}
_n_child_calls = 0
def lru_cache_pytest_save(*lru_cache_args, **lru_cache_kwargs):
"""like @lru_cache, but additionally clears lru_cache of this function in between pytest testcases"""
# if you want to switch _pytest_save off:
# def decorator(func):
# return lru_cache(func)
# return decorator
def decorator(func):
func_cached = lru_cache(func)
@wraps(func)
def wrapper(*args, **kwargs):
pytest_current = os.environ.get("PYTEST_CURRENT_TEST")
if _pytest_cache_func.get(func_cached.__name__) != pytest_current:
func_cached.cache_clear()
_pytest_cache_func[func_cached.__name__] = pytest_current
return func_cached(*args, **kwargs)
return wrapper
return decorator
@lru_cache_pytest_save(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
def reset_n_calls_fct_child():
global _n_child_calls
_n_child_calls = 0由于模块名略有不同,您需要在
test_main_fixed.py
# test_main_fixed.py
from my_lib_fixed 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_fixed.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_fixed.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")现在,所有4个命令都可以工作了:
$ python test_main.py
$ pytest test_main_fixed.py::test_1
$ pytest test_main_fixed.py::test_2
$ pytest test_main_fixed.pyhttps://stackoverflow.com/questions/72222314
复制相似问题