Python mock:为inheritan模拟基类

2024-05-18 05:36:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在测试一个继承自另一个非常复杂的类的类,它有DB连接方法和一堆依赖项。我想模拟它的基类,以便能够很好地处理子类中定义的方法,但是在我从模拟类继承的那一刻,对象本身就变成了一个模拟,并丢失了它的所有方法。

我怎么能嘲笑一个超类?

这种情况大致可以概括为:

import mock

ClassMock = mock.MagicMock()

class RealClass(ClassMock):

    def lol(self):
        print 'lol'

real = RealClass()
real.lol()  # Does not print lol, but returns another mock

print real # prints <MagicMock id='...'>

这是一个简单的例子。实际发生的情况是RealClass扩展了AnotherClass,但我设法截取了AnotherClass,并用一个mock替换它。


Tags: 对象方法db定义情况基类子类mock
2条回答

这应该对你有用。

import mock

ClassMock = mock.MagicMock

class RealClass(ClassMock):

    def lol(self):
        print 'lol'

real = RealClass()
real.lol()  # Does not print lol, but returns another mock

print real # prints <MagicMock id='...'>

你不应该像过去那样通过这个类的实例。mock.MagicMock是一个类,所以直接传递它。

In [2]: inspect.isclass(mock.MagicMock)
Out[2]: True

这是我很长时间以来一直在努力解决的问题,但我想我终于找到了解决办法。

正如您已经注意到的,如果您试图用Mock替换基类,那么您试图测试的类将变成Mock,这将破坏您测试它的能力。解决方案是只模拟基类的方法,而不是整个基类本身,但这说起来容易做起来难:在逐个测试的基础上逐个模拟每个方法很容易出错。

相反,我所做的是创建一个扫描另一个类的类,并将与另一个类上的方法匹配的Mock()赋值给它自己。然后可以在测试中使用这个类代替真正的基类。

这是假班:

class Fake(object):
    """Create Mock()ed methods that match another class's methods."""

    @classmethod
    def imitate(cls, *others):
        for other in others:
            for name in other.__dict__:
                try:
                    setattr(cls, name, Mock())
                except (TypeError, AttributeError):
                    pass
        return cls

例如,您可能有一些这样的代码(抱歉,这有点做作,假设BaseClassSecondClass正在做一些非琐碎的工作,包含许多方法,甚至根本不一定由您定义):

class BaseClass:
    def do_expensive_calculation(self):
        return 5 + 5

class SecondClass:
    def do_second_calculation(self):
        return 2 * 2

class MyClass(BaseClass, SecondClass):
    def my_calculation(self):
        return self.do_expensive_calculation(), self.do_second_calculation()

然后您就可以编写如下测试:

class MyTestCase(unittest.TestCase):
    def setUp(self):
        MyClass.__bases__ = (Fake.imitate(BaseClass, SecondBase),)

    def test_my_methods_only(self):
        myclass = MyClass()
        self.assertEqual(myclass.my_calculation(), (
            myclass.do_expensive_calculation.return_value, 
            myclass.do_second_calculation.return_value,
        ))
        myclass.do_expensive_calculation.assert_called_once_with()
        myclass.do_second_calculation.assert_called_once_with()

因此,基类上存在的方法仍然可用作您可以交互的mock,但是您的类本身并没有成为mock。

我很小心地确保这在python2和python3中都有效。

相关问题 更多 >