web-dev-qa-db-ja.com

__init__が正しい引数で呼び出されたことを主張する

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)
17
Srikanth

したがって、表示されるエラーメッセージは、実際にはモックを適切にチェックしていないためです。ここで理解しなければならないのは、デコレータで最終的に言っているのは、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__()の呼び出しの内部を調べるためにneedになります。これがなぜなのかわからない場合は、コンテキストマネージャーを読んでください。

したがって、それを念頭に置いて、次のようにチェックを実行するだけです。

_mock_obj.__enter__().install.assert_called_once_with()
_
17
idjaw

問題は、Installer(...)が_Installer.__init___を直接呼び出さないことです。むしろ、Installertypeのインスタンスであり、_type.__call___が定義されているため、Installer()は次と同等になります。

_type.__call__(Installer, ...)
_

その結果、Installer.__new__(Installer, ...)が呼び出され、その戻り値xが元の引数とともに_Installer.__init___に渡されます。

つまり、Installerにバインドするモックオブジェクトはtypeのインスタンスではないため、上記のいずれも当てはまりません。 Installer(...)は単にモックオブジェクトの呼び出しであるため、次のことを直接確認します。

_mock_installer.assert_called_once_with('foo', 'bar', 1)
_
2
chepner