簡體   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