【问题标题】:Avoid executing __init__ of mocked class避免执行模拟类的 __init__
【发布时间】:2014-11-05 15:12:04
【问题描述】:

我有一个带有昂贵 __init__ 函数的课程。我不想从测试中调用这个函数。

出于本示例的目的,我创建了一个在__init__ 中引发异常的类:

class ClassWithComplexInit(object):

    def __init__(self):
        raise Exception("COMPLEX!")

    def get_value(self):
        return 'My value'

我有第二个类,它构造ClassWithComplexInit 的实例并使用它的函数。

class SystemUnderTest(object):

    def my_func(self):
        foo = ClassWithComplexInit()
        return foo.get_value()

我正在尝试围绕SystemUnderTest#my_func() 编写一些单元测试。我遇到的问题是,无论我如何尝试模拟 ClassWithComplexInit__init__ 函数总是会被执行并引发异常。

class TestCaseWithoutSetUp(unittest.TestCase):

    @mock.patch('mypackage.ClassWithComplexInit.get_value', return_value='test value')
    def test_with_patched_function(self, mockFunction):
        sut = SystemUnderTest()
        result = sut.my_func()  # fails, executes ClassWithComplexInit.__init__()
        self.assertEqual('test value', result)

    @mock.patch('mypackage.ClassWithComplexInit')
    def test_with_patched_class(self, mockClass):
        mockClass.get_value.return_value = 'test value'
        sut = SystemUnderTest()
        result = sut.my_func()  # seems to not execute ClassWithComplexInit.__init__()
        self.assertEqual('test value', result)  # still fails
        # AssertionError: 'test value' != <MagicMock name='ClassWithComplexInit().get_value()' id='4436402576'>

上面的第二种方法是我从this similar Q&A 得到的,但它也不起作用。 似乎没有运行 __init__ 函数,但我的断言失败了,因为结果最终是一个模拟实例,而不是我的值。

我还尝试在setUp 函数中配置patch 实例,将startstop 函数用作the docs suggest

class TestCaseWithSetUp(unittest.TestCase):

    def setUp(self):
        self.mockClass = mock.MagicMock()
        self.mockClass.get_value.return_value = 'test value'
        patcher = mock.patch('mypackage.ClassWithComplexInit', self.mockClass)
        patcher.start()
        self.addCleanup(patcher.stop)

    def test_my_func(self):
        sut = SystemUnderTest()
        result = sut.my_func()  # seems to not execute ClassWithComplexInit.__init__()
        self.assertEqual('test value', result)  # still fails
        # AssertionError: 'test value' != <MagicMock name='mock().get_value()' id='4554658128'>

这似乎也避免了我的__init__ 函数,但我为get_value.return_value 设置的值没有得到尊重,get_value() 仍然返回一个MagicMock 实例。

如何模拟具有复杂__init__ 的类,该类由我的测试代码实例化?理想情况下,我想要一个适用于 TestCase 类中的许多单元测试的解决方案(例如,不需要 patch 每次测试)。

我正在使用 Python 版本2.7.6

【问题讨论】:

  • 如何使ClassWithComplexInit 可用于您的测试脚本? from mypackage import ClassWithComplexInit,还是别的什么?
  • @chepner 我的测试脚本甚至没有导入ClassWithComplexInit。我将一堆代码从我的真实代码中切换到此处发布在 SO 上并弄乱了包引用,我意识到引用是错误的。不过,您在回答中解决了这个问题,谢谢!

标签: python python-2.7 mocking python-unittest python-mock


【解决方案1】:

首先,您需要使用与您正在修补的名称相同的名称来创建foo,即,

class SystemUnderTest(object):

    def my_func(self):
        foo = mypackage.ClassWithComplexInit()
        return foo.get_value()

其次,您需要配置正确的模拟对象。您正在配置ClassWithComplexInit.get_value,这是未绑定的方法,但您需要配置ClassWithComplexInit.return_value.get_value,即Mock对象,它实际上将与foo.get_value()一起调用。

@mock.patch('mypackage.ClassWithComplexInit')
def test_with_patched_class(self, mockClass):
    mockClass.return_value.get_value.return_value = 'test value'
    sut = SystemUnderTest()
    result = sut.my_func()  # seems to not execute ClassWithComplexInit.__init__()
    self.assertEqual('test value', result)  # still fails

【讨论】:

  • 这成功了!我也在the docs for mocking chained calls 中遇到过这种方法。我正要自己发布解决方案,但你打败了我。
  • 另外,表达式mockClass.return_value.get_value.return_value 可以替换为mockClass().get_value.return_value,它仍然有效。
  • 哦,对了。模拟不知道它正在填充一个类,因此对ClassWithComplexInit.__new__ 的每个“调用”都将返回相同的对象,并且设置一个对象的get_value() 方法的返回值对所有对象都这样做.
猜你喜欢
  • 1970-01-01
  • 2019-10-31
  • 1970-01-01
  • 2023-02-10
  • 2020-09-23
  • 2015-11-27
  • 1970-01-01
  • 1970-01-01
  • 2014-12-10
相关资源
最近更新 更多