当通过引用它的变量调用方法时,Unittest .called 不起作用

Unittest .called doesn't work when the method is called through a variable that's referencing it

我的代码中有这样的东西:

def submethod_a():
    pass

def submethod_b():
    pass

_method_registry = {
    "a": submethod_a,
    "b": submethod_b,
}

def main_method(key):
    method_to_use = _method_registry[key]
    method_to_use()

然后我的测试中有这样的东西:

from unittest.mock import patch


@patch("submethod_b")
def test_correct_method_and_arguments(mocked_submethod_b):
    main_method(key)

    assert mocked_submethod_b.called

测试失败。我这样做是为了测试:

def main_method(key):
    if key == "a":
        submethod_a()
    else:
        submethod_b()

然后测试成功。

所以,.called 方法在引用中时不起作用? 这是预期的行为吗?我能做什么? 如果我不以任何其他方式解决它,我想我会重构代码并使用 类 和 2 种不同的“子方法”实现,我认为这应该有效。

谢谢。

最初阅读源代码时,它已经将原始 submethod_b 函数存储在变量 _method_registry 中,因此当您在 运行时 [=56] 对其进行修补时=],虽然函数被替换了,但并没有改变这个变量中已经存储的值。为了证明这一点,让我们添加打印件。

main.py

def submethod_a():
    pass

def submethod_b():
    pass

_method_registry = {
    "a": submethod_a,
    "b": submethod_b,
}


def main_method(key):
    print(_method_registry["b"], "- Stored function")
    print(submethod_b, "- Patched function")

test_main.py

from unittest.mock import patch

from main import main_method


def test_no_patch():
    main_method("b")


@patch("main.submethod_b")
def test_with_patch(mocked_submethod_b):
    main_method("b")

输出

$ pytest -q -rP
================================================================================================= PASSES ==================================================================================================
______________________________________________________________________________________________ test_no_patch ______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
<function submethod_b at 0x7fba0fb63ee0> - Stored function
<function submethod_b at 0x7fba0fb63ee0> - Patched function
_____________________________________________________________________________________________ test_with_patch _____________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
<function submethod_b at 0x7fba0fb63ee0> - Stored function
<MagicMock name='submethod_b' id='140437103934432'> - Patched function
2 passed in 0.04s

如你所见,没有打补丁,这个值和原来的函数是一样的。但是对于补丁,请注意只有实际功能被替换,而不是已经存储的功能。

备选方案

  1. 将子方法函数放入其自己的文件中。
  2. 将子方法导入主文件并按原样使用。
  3. 修补子方法文件。
  4. 重新加载主文件,以便在重新创建 _method_registry 时使用修补的子方法。

main.py

from submethod import submethod_a, submethod_b  # Or: import submethod

_method_registry = {
    "a": submethod_a,  # Depending on chosen import style, this can be: submethod.submethod_a
    "b": submethod_b,  # Depending on chosen import style, this can be: submethod.submethod_b
}


def main_method(key):
    print(_method_registry["b"], "- Stored function")
    print(submethod_b, "- Patched function")

    method_to_use = _method_registry[key]
    method_to_use()

submethod.py

def submethod_a():
    pass

def submethod_b():
    pass

test_main.py

from importlib import reload
import sys

from unittest.mock import patch

from main import main_method


@patch("submethod.submethod_b")
def test_with_patch(mocked_submethod_b):
    # Now that the patch is  in effect and replaced the submethod.submethod_b, reload the main file so that it recreates _method_registry with the patched function
    reload(sys.modules['main'])

    main_method("b")

    assert mocked_submethod_b.called

输出

$ pytest -q -rP
================================================================================================= PASSES ==================================================================================================
_____________________________________________________________________________________________ test_with_patch _____________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
<MagicMock name='submethod_b' id='140432453077360'> - Stored function
<MagicMock name='submethod_b' id='140432453077360'> - Patched function
1 passed in 0.04s

现在,存储函数和修补函数本身都指向模拟版本。因此,.called 断言现在成功了。