簡體   English   中英

Python 返回 MagicMock 對象而不是 return_value

[英]Python returns MagicMock object instead of return_value

我有一個 python 文件a.py ,其中包含兩個類AB

class A(object):
    def method_a(self):
        return "Class A method a"

class B(object):
    def method_b(self):
        a = A()
        print a.method_a()

我想通過模擬AB類中對method_b進行單元測試。 這是用於此目的的文件testa.py的內容:

import unittest
import mock
import a


class TestB(unittest.TestCase):

    @mock.patch('a.A')
    def test_method_b(self, mock_a):
        mock_a.method_a.return_value = 'Mocked A'
        b = a.B()
        b.method_b()


if __name__ == '__main__':
    unittest.main()

我希望在輸出中得到Mocked A 但我得到的是:

<MagicMock name='A().method_a()' id='4326621392'>

我在哪里做錯了?

當您@mock.patch('a.A')時,您正在用mock_a替換被測代碼中的類A

然后在B.method_b設置a = A() ,現在a = mock_a() - 即amock_areturn_value 由於您尚未指定此值,因此它是常規MagicMock 這也沒有配置,所以在調用它的方法時你會得到默認響應(又一個MagicMock )。

相反,您希望將mock_areturn_value配置為具有適當的方法,您可以這樣做:

mock_a().method_a.return_value = 'Mocked A' 
    # ^ note parentheses

或者,也許更明確:

mock_a.return_value.method_a.return_value = 'Mocked A'

您的代碼在a = A (分配類,而不是創建實例)的情況下可以工作,因為那時a.method_a()會觸發您的模擬方法。

我更喜歡用測試器夾具進行 pytest 這是使用pytest和mocker的相同測試:

import a


class TestB:
    def test_method_b(self, mocker):
        mock_A = mocker.MagicMock(name='A', spec=a.A)
        mocker.patch('a.A', new=mock_A)
        mock_A.return_value.method_a.return_value = 'Mocked A'

        b = a.B()
        b.method_b()

您可能會發現我編寫測試的方式比測試本身更有趣 - 我創建了一個python庫來幫助我學習語法。

以下是我如何以系統的方式處理您的問題:

我們從你想要的測試和我的幫助庫開始:

import a

from mock_autogen.pytest_mocker import PytestMocker


class TestB:
    def test_method_b(self, mocker):
        # this would output the mocks we need
        print(PytestMocker(a).mock_classes().prepare_asserts_calls().generate())

        # your original test, without the mocks
        b = a.B()
        b.method_b()

現在測試沒有做太多,但打印輸出很有用:

# mocked classes
mock_A = mocker.MagicMock(name='A', spec=a.A)
mocker.patch('a.A', new=mock_A)
mock_B = mocker.MagicMock(name='B', spec=a.B)
mocker.patch('a.B', new=mock_B)
# calls to generate_asserts, put this after the 'act'
import mock_autogen
print(mock_autogen.generator.generate_asserts(mock_A, name='mock_A'))
print(mock_autogen.generator.generate_asserts(mock_B, name='mock_B'))

現在,我在調用B()和之后的generate_asserts部分之前為A放置了一個模擬器,就像這樣(不需要先前的打印,所以我將其刪除):

def test_method_b(self, mocker):
    # mocked classes
    mock_A = mocker.MagicMock(name='A', spec=a.A)
    mocker.patch('a.A', new=mock_A)

    # your original test, without the mocks
    b = a.B()
    b.method_b()

    # calls to generate_asserts, put this after the 'act'
    import mock_autogen
    print(mock_autogen.generator.generate_asserts(mock_A, name='mock_A'))

執行此測試后,我們獲得了一些有價值的輸入:

assert 1 == mock_A.call_count
mock_A.assert_called_once_with()
mock_A.return_value.method_a.assert_called_once_with()
mock_A.return_value.method_a.return_value.__str__.assert_called_once_with()

前兩行驗證A mock是否已初始化一次,沒有參數。 第三行驗證了method_a被調用,而第四行可能對你最有幫助,並且可以節省你很多時間自己解決這個問題:

mock_A.return_value.method_a.return_value.__str__.assert_called_once_with()

您會看到method_a的返回值已應用於str (由於print功能)。 用你想要的字符串替換它很容易:

mock_A.return_value.method_a.return_value = 'Mocked A'

這就是我如何進入上面提到的完整測試方法。

在模擬對象的情況下,我使用以下語法:

@mock.patch.object(
    a.A,
    'method_a',
    lambda a: "Mocked A")
def test_method_b(self):
    b = a.B()
    b.method_b()

在這種情況下,method_a 被 lambda 函數模擬。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM