断言__init__是用正确的参数调用的

时间:2016-02-29 23:40:00

标签: python testing mocking python-unittest

我正在使用python模拟声明某个特定对象是使用正确的参数创建的。这就是我的代码的样子:

class Installer:
    def __init__(foo, bar, version):
        # Init stuff
        pass
    def __enter__(self):
        return self

    def __exit__(self, type, value, tb):
        # cleanup
        pass

    def install(self):
        # Install stuff
        pass

class Deployer:
    def deploy(self):
        with Installer('foo', 'bar', 1) as installer:
            installer.install()

现在,我想声明installer是使用正确的参数创建的。这是我到目前为止的代码:

class DeployerTest(unittest.TestCase):
    @patch('Installer', autospec=True)
    def testInstaller(self, mock_installer):
        deployer = Deployer()
        deployer.deploy()

        # Can't do this :-(
        mock_installer.__init__.assert_called_once_with('foo', 'bar', 1)

这是我得到的错误:

  File "test_deployment.py", line .., in testInstaller
    mock_installer.__init__.assert_called_once_with('foo', 'bar', 1)
AttributeError: 'function' object has no attribute 'assert_called_once_with'

这是固定代码(称之为test.py)。谢谢,全部!

import unittest
from mock import patch

class Installer:
    def __init__(self, foo, bar, version):
        # Init stuff
        pass

    def __enter__(self):
        return self

    def __exit__(self, type, value, tb):
        # cleanup
        pass

    def install(self):
        # Install stuff
        pass

class Deployer:
    def deploy(self):
        with Installer('foo', 'bar', 1) as installer:
            installer.install()

class DeployerTest(unittest.TestCase):
    @patch('tests.test.Installer', autospec=True)
    def testInstaller(self, mock_installer):
        deployer = Deployer()
        deployer.deploy()

        # Can't do this :-(
        # mock_installer.__init__.assert_called_once_with('foo', 'bar', 1)

        # Try this instead
        mock_installer.assert_called_once_with('foo', 'bar', 1)

2 个答案:

答案 0 :(得分:13)

因此,您获得的错误消息实际上是因为您没有正确检查您的模拟。你在这里要理解的是,在你的装饰者中,你最终会这样说,对Installer的调用会改为使用Mock对象。

因此,对于您要修补的Installer()的任何调用,其返回值将调用Mock()

因此,您实际想要检查的断言仅在mock_installer,而不是mock_installer.__init__.

mock_installer.assert_called_once_with('foo', 'bar', 1)

以下是对您的代码所做的修改:

class DeployerTest(unittest.TestCase):

    @patch('Installer', autospec=True)
    def testInstaller(self, mock_installer):
        deployer = Deployer()
        deployer.deploy()

        mock_installer.assert_called_once_with('foo', 'bar', 1)

提供更多解释的一些额外信息,如果你现在正在测试是否在你的上下文管理器中调用了install,你必须在这里意识到你实际上必须检查你的__enter__,所以一个结构会是这样的:

为清楚起见,在测试方法中创建一个mock_obj并且:

mock_obj = mock_installer.return_value

现在,在您的上下文管理器中,您将需要查看对__enter__()的调用。如果你不知道为什么会这样,请阅读上下文管理器。

因此,考虑到这一点,您只需执行以下检查:

mock_obj.__enter__().install.assert_called_once_with()

答案 1 :(得分:2)

问题是Installer(...)没有直接调用Installer.__init__;相反,因为Installertype的一个实例,并且定义了type.__call__,所以Installer()等同于<{p}}

type.__call__(Installer, ...)

会调用Installer.__new__(Installer, ...),其返回值x会与原始参数一起传递给Installer.__init__

所有这一切都是因为绑定到Installer的模拟对象不是type的实例,所以前面没有一个适用。 Installer(...)只是对模拟对象的调用,因此您可以直接检查它:

mock_installer.assert_called_once_with('foo', 'bar', 1)