web-dev-qa-db-ja.com

pythonクラスのパッチをモックして、インスタンス化ごとに新しいMockオブジェクトを取得するにはどうすればよいですか?

OK、
これは manual で言及されていることを知っており、おそらくside_effectreturn_valueと関係があるはずですが、簡単で直接的な例が役立ちます非常に。

私が持っています:

class ClassToPatch():
   def __init__(self, *args):
       _do_some_init_stuff()

   def some_func():
       _do_stuff()


class UUT():
    def __init__(self, *args)
       resource_1 = ClassToPatch()
       resource_2 = ClassToPatch()

ここで、UUTクラスを単体テストし、ClassToPatchをモックしたいと思います。 UUTクラスが2つのClassToPatchオブジェクトをインスタンス化することを知っているので、Mockフレームワークがインスタンス化ごとに新しいMockオブジェクトを返すようにしたいので、後でそれぞれ個別に呼び出しをアサートできます。

テストケースで@patchデコレータを使用してこれを実現するにはどうすればよいですか?つまり、次のコードサンプルを修正するにはどうすればよいですか?

class TestCase1(unittest.TestCase):

    @patch('classToPatch.ClassToPatch',autospec=True)
    def test_1(self,mock1,mock2):
        _assert_stuff()
26
bavaza

ここにあなたを始めるための簡単な例があります:

import mock
import unittest

class ClassToPatch():
   def __init__(self, *args):
       pass

   def some_func(self):
       return id(self)

class UUT():
    def __init__(self, *args):
        resource_1 = ClassToPatch()
        resource_2 = ClassToPatch()
        self.test_property = (resource_1.some_func(), resource_2.some_func())

class TestCase1(unittest.TestCase):
    @mock.patch('__main__.ClassToPatch', autospec = True)
    def test_1(self, mock1):
        ctpMocks = [mock.Mock(), mock.Mock()]
        ctpMocks[0].some_func.return_value = "funky"
        ctpMocks[1].some_func.return_value = "monkey"
        mock1.side_effect = ctpMocks

        u = UUT()
        self.assertEqual(u.test_property, ("funky", "monkey"))

if __name__ == '__main__':
    unittest.main()

私は追加しました test_propertyをUUTに追加して、単体テストで有用な処理を行う今、モックなしtest_propertyは、2つのClassToPatchインスタンスのIDを含むタプルである必要があります。しかし、モックではタプルになるはずです:("funky", "monkey")

side_effect モックオブジェクトのプロパティ。これにより、ClassToPatchイニシャライザーの各呼び出しでUUTの異なるインスタンスが返されます。

お役に立てれば。

編集:ところで、ユニットテストを実行すると、次のようになります。

.
----------------------------------------------------------------------
Ran 1 test in 0.004s

OK
28
srgerg

これは、作成された任意の数のインスタンスを処理するためのより一般的な別のバージョンです。

class TestUUT:
    def test_init(self, mocker):
        class MockedClassToPatchMeta(type):
            static_instance = mocker.MagicMock(spec=ClassToPatch)

            def __getattr__(cls, key):
                return MockedClassToPatchMeta.static_instance.__getattr__(key)

        class MockedClassToPatch(metaclass=MockedClassToPatchMeta):
            original_cls = ClassToPatch
            instances = []

            def __new__(cls, *args, **kwargs):
                MockedClassToPatch.instances.append(
                    mocker.MagicMock(spec=MockedClassToPatch.original_cls))
                MockedClassToPatch.instances[-1].__class__ = MockedClassToPatch
                return MockedClassToPatch.instances[-1]

        mocker.patch(__name__ + '.ClassToPatch', new=MockedClassToPatch)

        UUT()

        # since your original code created two instances
        assert 2 == len(MockedClassToPatch.instances)

各インスタンスに対してより完全な検証が必要な場合は、MockedClassToPatch.instances[0]またはMockedClassToPatch.instances[1]にアクセスできます。

また、メタクラスのボイラープレートを生成するために ヘルパーライブラリ を作成しました。私が書いたあなたの例に必要なコードを生成するために:

print(PytestMocker(mocked=ClassToPatch, name=__name__).mock_classes().mock_classes_static().generate())
0
Peter K