Python 返回 MagicMock 对象而不是 return_value
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38199008/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python returns MagicMock object instead of return_value
提问by Mehdi Jafarnia Jahromi
I have a python file a.py
which contains two classes A
and B
.
我有一个 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()
I would like to unittest method_b
in class B
by mocking A
. Here is the content of the file testa.py
for this purpose:
我想通过模拟method_b
在课堂上B
进行单元测试A
。这是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()
I expect to get Mocked A
in the output. But what I get is:
我希望得到Mocked A
输出。但我得到的是:
<MagicMock name='A().method_a()' id='4326621392'>
Where am I doing wrong?
我哪里做错了?
回答by jonrsharpe
When you @mock.patch('a.A')
, you are replacing the class A
in the code under test with mock_a
.
当您@mock.patch('a.A')
,您要更换类A
下与测试代码mock_a
。
In B.method_b
you then set a = A()
, which is now a = mock_a()
- i.e. a
is the return_value
of mock_a
. As you haven't specified this value, it's a regular MagicMock
; this isn't configured either, so you get the default response (yet another MagicMock
) when calling methods on it.
在B.method_b
你接盘a = A()
,这是现在a = mock_a()
-即a
是return_value
的mock_a
。由于您尚未指定此值,因此它是一个常规MagicMock
; 这也未配置,因此MagicMock
在调用其上的方法时会获得默认响应(还有另一个)。
Instead, you want to configure the return_value
of mock_a
to have the appropriate method, which you can do as either:
相反,您希望将return_value
ofmock_a
配置为具有适当的方法,您可以执行以下任一操作:
mock_a().method_a.return_value = 'Mocked A'
# ^ note parentheses
or, perhaps more explicitly:
或者,也许更明确:
mock_a.return_value.method_a.return_value = 'Mocked A'
Your code would have worked in the case a = A
(assigning the class, not creating an instance), as then a.method_a()
would have triggered your mock method.
您的代码在这种情况下会起作用a = A
(分配类,而不是创建实例),因为那样a.method_a()
会触发您的模拟方法。
回答by Peter K
I prefer pytestwith mocker fixture. Here is the same test, using pytest and mocker:
我更喜欢带有mocker fixture 的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()
You may find the way I wrote the test more interesting than the test itself - I have created a python libraryto help me with the syntax.
您可能会发现我编写测试的方式比测试本身更有趣——我创建了一个python 库来帮助我处理语法。
Here is how I approached your problem in a systematic way:
以下是我系统地解决您的问题的方法:
We start with the test you want and my helper library:
我们从你想要的测试和我的帮助库开始:
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()
Now the test doesn't do much, but the print output is useful:
现在测试没有做太多,但打印输出很有用:
# 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'))
Now, I'm placing a single mock for A
before the call to B()
and the generate_asserts
section after, like so (no need for the previous print, so I removed it):
现在,我A
在调用之前B()
和generate_asserts
之后放置一个模拟,就像这样(不需要以前的打印,所以我删除了它):
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'))
After this test execution we have gained some valuable input:
执行此测试后,我们获得了一些有价值的输入:
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()
The first two lines verifies the A
mock was initialized once, without parameters. The third line verifies method_a
was called, while the 4th line may be the most helpful for you and could have saved you much time figuring this on your own:
前两行验证A
模拟已初始化一次,没有参数。method_a
调用了第三行验证,而第 4 行可能对您最有帮助,并且可以为您节省很多时间来自己解决这个问题:
mock_A.return_value.method_a.return_value.__str__.assert_called_once_with()
You see that the returned value of method_a
has been applied with str
(due to the print
function). replacing that with your desired string is pretty easy:
您会看到 的返回值method_a
已被应用str
(由于print
函数)。用您想要的字符串替换它非常简单:
mock_A.return_value.method_a.return_value = 'Mocked A'
And that's how I got to the full test method mentioned above.
这就是我如何获得上面提到的完整测试方法。