如何模拟修补 Python 类并为每个实例化获取一个新的 Mock 对象?

问题描述 投票:0回答:2

好的,
我知道这在手册中提到过,可能与

side_effect
和/或
return_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
类将实例化两个
ClassToPatch
对象,我希望 Mock 框架为每个实例化返回一个新的 Mock 对象,这样我以后就可以分别对每个对象进行断言调用。

如何在测试用例中使用

@patch
装饰器来实现此目的?即,如何修复以下代码示例?

class TestCase1(unittest.TestCase):

    @patch('classToPatch.ClassToPatch',autospec=True)
    def test_1(self,mock1,mock2):
        _assert_stuff()
python mocking python-unittest python-mock
2个回答
38
投票

这是一个快速入门的示例:

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
应该是一个包含两个
ClassToPatch
实例的 id 的元组。但对于模拟来说,它应该是元组:
("funky", "monkey")

我使用了模拟对象的

side_effect
属性,以便在
ClassToPatch
初始化程序中的每次调用时返回
UUT
的不同实例。

希望这有帮助。

编辑:哦,顺便说一句,当我运行单元测试时,我得到:

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

OK

3
投票

这是另一个版本,它更通用,可以处理创建的任意数量的实例:

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())
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.