[英]Python returns MagicMock object instead of return_value
我有一個 python 文件a.py
,其中包含兩個類A
和B
。
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()
我想通過模擬A
在B
類中對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()
- 即a
是mock_a
的return_value
。 由於您尚未指定此值,因此它是常規MagicMock
; 這也沒有配置,所以在調用它的方法時你會得到默認響應(又一個MagicMock
)。
相反,您希望將mock_a
的return_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.