Python返回的是MagicMock对象,而不是return_value。

161

我有一个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()

我想通过模拟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'>

我哪里做错了?


1
当进行测试时,A()mock_A 返回 return_value(一个普通的 MagicMock,因为您没有指定其他内容),它不是类 A 的实例。 您需要将 return_value 设置为具有定义的 method_a 的内容。 - jonrsharpe
4
mock_a.method_a.return_value = 'Mocked A' => mock_a().method_a.return_value = 'Mocked A' 这样更好哦 :) - Ali SAID OMAR
2
@jonrsharpe。感谢您的解释。我刚刚尝试了。mock_a().method_a.return_value = 'Mocked A'mock_a.return_value.method_a.return_value = 'Mocked A'都可以工作。非常感谢您的评论。请问您能否将其作为答案发布吗? - Mehdi Jafarnia Jahromi
@MehdiJafarniaJahromi 非常感谢! - Niakros
@Niakros,不用谢 :) - Mehdi Jafarnia Jahromi
显示剩余2条评论
3个回答

203
当你使用@mock.patch('a.A')时,你将代码中的类A替换为mock_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() 会触发你的模拟方法。


@jonrsharpe,是的,我可以这样做,但我也在我的调用方法中执行了df.drop,我需要进行断言,并且我没有从调用方法返回数据框。这会造成问题。我发现使用mock_data.configure_mock(columns='my_column')的方法可以解决这个问题。不过还是谢谢你的回复。(参考:https://bradmontgomery.net/blog/how-world-do-you-mock-name-attribute/) - Mr. Unnormalized Posterior
2
这个解释比任何Python文档都好。谢谢! - Sharanya
2
如果你想知道它为什么起作用...那是因为mock_a()返回了一个单例。 - Sławomir Lenart
2
我已经设置了 return_value,但是 mock 仍然被返回。这些模拟不容易正确设置。 - WestCoastProjects
2
第一种解决方案:mock_a().method_a.return_value,以这种方式调用mock_a,即使被测试的代码从未调用mock_amock_a.assert_called()也将变为true。 第二种解决方案:mock_a.return_value.method_a.return_value,不以那种方式调用mock_amock_a.assert_called()仍为false)。因此,我更喜欢第二种解决方案。 - Rainbolt
显示剩余7条评论

3

在模拟对象的情况下,我会使用以下语法:

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

在这种情况下,lambda函数会模拟方法method_a。

0
在我的情况下,我的 `return_value` 是这个 {"key1": "value1"}。 因此,在应用补丁之前,我需要使用 dict() 进行转换。
像这样:
@fixture
def mock_something(mocker: MockFixture, request: SubRequest) -> MagicMock:
    method_to_mock = f"your_module.YourClass.some_method"
    return_value = dict(request.param)
    if isinstance(return_value, Exception):
        return mocker.patch(method_to_mock, side_effect=return_value)
    return mocker.patch(method_to_mock, return_value=return_value)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接