[英]Python returns MagicMock object instead of return_value
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
.我想通过模拟
A
在B
类中对method_b
进行单元测试。 Here is the content of the file testa.py
for this purpose:这是用于此目的的文件
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.我希望在输出中得到
Mocked A
But what I get is:但我得到的是:
<MagicMock name='A().method_a()' id='4326621392'>
Where am I doing wrong?我在哪里做错了?
When you @mock.patch('a.A')
, you are replacing the class A
in the code under test with mock_a
.当您
@mock.patch('a.A')
时,您正在用mock_a
替换被测代码中的类A
。
In B.method_b
you then set a = A()
, which is now a = mock_a()
- ie a
is the return_value
of mock_a
.然后在
B.method_b
设置a = A()
,现在a = mock_a()
- 即a
是mock_a
的return_value
。 As you haven't specified this value, it's a regular MagicMock
;由于您尚未指定此值,因此它是常规
MagicMock
; this isn't configured either, so you get the default response (yet another MagicMock
) when calling methods on it.这也没有配置,所以在调用它的方法时你会得到默认响应(又一个
MagicMock
)。
Instead, you want to configure the return_value
of mock_a
to have the appropriate method, which you can do as either:相反,您希望将
mock_a
的return_value
配置为具有适当的方法,您可以这样做:
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()
会触发您的模拟方法。
I prefer pytest with mocker fixture . 我更喜欢用测试器夹具进行 pytest 。 Here is the same test, using pytest and mocker:
这是使用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 library to 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): 现在,我在调用
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'))
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. 前两行验证
A
mock是否已初始化一次,没有参数。 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: 第三行验证了
method_a
被调用,而第四行可能对你最有帮助,并且可以节省你很多时间自己解决这个问题:
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). 您会看到
method_a
的返回值已应用于str
(由于print
功能)。 replacing that with your desired string is pretty easy: 用你想要的字符串替换它很容易:
mock_A.return_value.method_a.return_value = 'Mocked A'
And that's how I got to the full test method mentioned above. 这就是我如何进入上面提到的完整测试方法。
In case of mocking an object I use this syntax:在模拟对象的情况下,我使用以下语法:
@mock.patch.object(
a.A,
'method_a',
lambda a: "Mocked A")
def test_method_b(self):
b = a.B()
b.method_b()
In this case the method_a is mocked by the lambda function.在这种情况下,method_a 被 lambda 函数模拟。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.