Python 如何模拟一个非';你不叫名字吗?
假设我有一个装饰器,它收集它装饰的所有函数,以便在将来的某个时候调用 mydecorator.pyPython 如何模拟一个非';你不叫名字吗?,python,mocking,pytest,python-decorators,pytest-mock,Python,Mocking,Pytest,Python Decorators,Pytest Mock,假设我有一个装饰器,它收集它装饰的所有函数,以便在将来的某个时候调用 mydecorator.py class CallLater(object): funcs = [] def __init__(self, func): self.funcs.append(func) @classmethod def call_now(cls, *args, **kwargs): for func in cls.funcs:
class CallLater(object):
funcs = []
def __init__(self, func):
self.funcs.append(func)
@classmethod
def call_now(cls, *args, **kwargs):
for func in cls.funcs:
func(*args, **kwargs)
import logging
from mydecorator import CallLater
logging.basicConfig(level=logging.INFO)
@CallLater
def log_a():
logging.info("A")
@CallLater
def log_b():
logging.info("B")
def log_c():
logging.info("C")
import logging
import pytest
from mymodule import log_a, log_c
from mydecorator import CallLater
logging.basicConfig(level=logging.INFO)
pytest_plugins = ('pytest_mock',)
def test_mocking(mocker, caplog):
mocker.patch('mymodule.log_b', log_c)
CallLater.call_now()
logs = [rec.message for rec in caplog.records]
assert logs == ["A", "C"]
然后,我在一个模块中有一个函数,其中一个将由我的decorator保存
mymodule.py
class CallLater(object):
funcs = []
def __init__(self, func):
self.funcs.append(func)
@classmethod
def call_now(cls, *args, **kwargs):
for func in cls.funcs:
func(*args, **kwargs)
import logging
from mydecorator import CallLater
logging.basicConfig(level=logging.INFO)
@CallLater
def log_a():
logging.info("A")
@CallLater
def log_b():
logging.info("B")
def log_c():
logging.info("C")
import logging
import pytest
from mymodule import log_a, log_c
from mydecorator import CallLater
logging.basicConfig(level=logging.INFO)
pytest_plugins = ('pytest_mock',)
def test_mocking(mocker, caplog):
mocker.patch('mymodule.log_b', log_c)
CallLater.call_now()
logs = [rec.message for rec in caplog.records]
assert logs == ["A", "C"]
现在,如果我导入mymodule
并调用CallLater.call_Now()
,将调用log_a
和log_b
。但假设在测试期间,我希望用log\u c
替换log\u b
。我会试着用一个模拟来替换
mock_test.py
class CallLater(object):
funcs = []
def __init__(self, func):
self.funcs.append(func)
@classmethod
def call_now(cls, *args, **kwargs):
for func in cls.funcs:
func(*args, **kwargs)
import logging
from mydecorator import CallLater
logging.basicConfig(level=logging.INFO)
@CallLater
def log_a():
logging.info("A")
@CallLater
def log_b():
logging.info("B")
def log_c():
logging.info("C")
import logging
import pytest
from mymodule import log_a, log_c
from mydecorator import CallLater
logging.basicConfig(level=logging.INFO)
pytest_plugins = ('pytest_mock',)
def test_mocking(mocker, caplog):
mocker.patch('mymodule.log_b', log_c)
CallLater.call_now()
logs = [rec.message for rec in caplog.records]
assert logs == ["A", "C"]
但是当我运行pytest
时,我发现我的mock没有工作
FAILED mock_test.py::test_mocking - AssertionError: assert ['A', 'B'] == ['A', 'C']
我认为
'mymodule.log_b'
是错误的模拟目标,因为它没有被调用为mymodule.log_b()
,但我不确定在这种情况下应该使用什么。任何建议都将不胜感激 这是不可能的,问题是在加载时功能已经分配给列表。我能看到的唯一修补方法是直接修补CallLater.funcs
,这有点尴尬,因为您必须手动将log\u b
替换为log\u c
,但它是这样的:
import logging
from unittest import mock
from mymodule import log_c
from mydecorator import CallLater
logging.basicConfig(level=logging.INFO)
def test_mocking(caplog):
funcs = [log_c if f.__name__ == 'log_b' else f for f in CallLater.funcs]
with mock.patch.object(CallLater, 'funcs', funcs):
CallLater.call_now()
logs = [rec.message for rec in caplog.records]
assert logs == ["A", "C"]
请注意,您不能直接与函数进行比较(例如,
f==log\u b
),因为log\u b
是修饰函数,而不是保存在CallLater.funcs
中的函数 这很有道理。我没有考虑创建函数列表的时间。感谢您快速清晰的回复!很高兴我能帮忙!作为参考,这个问题的灵感来自于想要禁用,通过调用sqlalchemy.event.remove
似乎可以更好地处理这个问题,如中所示