我如何模拟未按名称调用的函数?

问题描述 投票:0回答:1

假设我有一个装饰器,它收集它装饰的所有函数,以备将来在某个时候调用。

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:
            func(*args, **kwargs)

然后,我在模块中有一个函数,其中一个将由我的装饰器保存。

mymodule.py

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")

现在,如果我导入mymodule并调用CallLater.call_now(),则将调用log_alog_b。但是,假设在测试期间,我希望将log_b替换为log_c。我将尝试用模拟进行替换。

mock_test.py

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时,我发现我的模拟无法正常工作。

FAILED mock_test.py::test_mocking - AssertionError: assert ['A', 'B'] == ['A', 'C']

我想像'mymodule.log_b'是错误的模拟目标,因为它没有作为mymodule.log_b()调用,但是我不确定在这种情况下该使用什么。任何建议表示赞赏!

python mocking pytest python-decorators pytest-mock
1个回答
0
投票

这样是不可能的,问题是在加载时已将功能分配给列表。我看到的对此补丁进行修补的唯一方法是直接对CallLater.funcs进行修补,这有点尴尬,因为您必须手动将log_b替换为log_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_b)进行比较,因为log_b是修饰的功能,而不是保存在CallLater.funcs中的功能。

© www.soinside.com 2019 - 2024. All rights reserved.