繁体   English   中英

python - 模拟 - class 方法模拟但未报告为被调用

[英]python - mock - class method mocked but not reported as being called

在这里学习 python 模拟。 我需要一些帮助来了解补丁在 mocking 和 class 时是如何工作的。

在下面的代码中,我模拟了一个 class。被测试的 function 接收模拟并在其上调用 function。 在我的断言中,class 被成功调用,但 function 被报告为未被调用。

我添加了调试打印以查看测试中的 function 中的内容,并报告为已调用。

我的期望是断言assert facadeMock.install.called应该是真的。 为什么它没有被报告为已调用,我该如何实现?

谢谢你。

安装/__init__.py

from .facade import Facade

def main():
    f = Facade()
    f.install()
    print('jf-debug-> "f.install.called": {value}'.format(
        value=f.install.called))

测试/install_tests.py

import os
import sys
# allow import of package
sys.path.insert(0,
                os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

from unittest.mock import patch

import install

@patch('install.Facade') # using autospec=True did not change the result
def test_main_with_links_should_call_facade_install_with_link_true(facadeMock):
    install.main()

    assert facadeMock.called
    assert facadeMock.install is install.Facade.install
    assert facadeMock.install.called   # <-------------------- Fails here!

output:

============================= test session starts ==============================
platform linux -- Python 3.10.6, pytest-7.2.0, pluggy-1.0.0
rootdir: /home/jfl/ubuntu-vim, configfile: pytest.ini
collected 1 item                                                               

test/install_tests.py jf-debug-> "f.install.called": True
F

=================================== FAILURES ===================================
________ test_main_with_links_should_call_facade_install_with_link_true ________

facadeMock = <MagicMock name='Facade' id='140679041900864'>

    @patch('install.Facade')
    def test_main_with_links_should_call_facade_install_with_link_true(facadeMock):
        install.main()
    
        assert facadeMock.called
    
        assert facadeMock.install is install.Facade.install
    
>       assert facadeMock.install.called
E       AssertionError: assert False
E        +  where False = <MagicMock name='Facade.install' id='140679042325216'>.called
E        +    where <MagicMock name='Facade.install' id='140679042325216'> = <MagicMock name='Facade' id='140679041900864'>.install

test/install_tests.py:21: AssertionError
=========================== short test summary info ============================
FAILED test/install_tests.py::test_main_with_links_should_call_facade_install_with_link_true - AssertionError: assert False
============================== 1 failed in 0.09s ===============================

[编辑]

感谢@chepner 和@Daniil Fajnberg 的评论。 我找到了问题的原因。

问题可以减少在: install/__init__.py在main() 中调用Facade() 时接收到一个Facade 的实例。 此实例与测试参数中收到的实例不同。 它们是不同的实例。

要检索在 main() 中接收到的实例,请执行以下操作:

    actualInstance = facadeMock.return_value
    assert actualInstance.install.called

它有效!

谢谢你。 这确实有助于我理解 python 中模拟的工作原理。

[/编辑]

我找到了解决您问题的方法; 它是经验性的,但它有效。

为了通过你的测试,我修改了它,如下所示:

@patch('install.Facade') # using autospec=True did not change the result
def test_main_with_links_should_call_facade_install_with_link_true(facadeMock):
    install.main()

    assert facadeMock.called
    assert facadeMock.install is install.Facade.install

    #assert facadeMock.install.called   # <-------------------- Fails here!
    install_called = False
    for call_elem in facadeMock.mock_calls:
        if call_elem[0] == "().install":
            install_called = True
            break
    assert install_called == True

模拟对象facadeMockf

facadeMock是在测试代码中创建的模拟 object ,在测试期间生产代码使用它通过指令创建模拟 object f

f = Facade()

在生产代码中, f是一个 mock object(即 class Mock的一个实例),因为它是由 Mock object Facade创建的,它正是facadeMock

但是ffacadeMock是 class Mock的两个不同实例。

下面我展示了facadeMockFacadef的 id 值:

facadeMock = <MagicMock name='Facade' id='140449467990536'>
Facade = <MagicMock name='Facade' id='140449467990536'>
f = <MagicMock name='Facade()' id='140449465274608'>

facadeMockFacade的 id 与f的 id 不同。

属性 mock_calls

当您的测试代码被执行时, function install.main()执行会导致模拟 object facadeMock的属性mock_calls的定义。

该属性是复杂元素的列表。
如果您检查每个元素的第一个字段(我的意思是 position 0 中的字段),您可以找到被调用的模拟方法的名称。

在您的情况下,您必须找到install并为此必须查找().install 因此,我的测试检查了mock_calls的所有元素,并且仅当().install被发现时才设置变量install_called=True

我希望这个答案可以帮助你。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM