bgu*_*ach 10 python inheritance unit-testing mocking
我正在测试一个继承自另一个非常复杂的类,使用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='...'>
Run Code Online (Sandbox Code Playgroud)
这是一个简化的案例.实际发生的是RealClass扩展AnotherClass,但我设法拦截AnotherClass并用模拟替换它.
rob*_*bru 15
这是我长期以来一直在努力的事情,但我想我终于找到了解决方案.
正如您已经注意到的,如果您尝试使用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
Run Code Online (Sandbox Code Playgroud)
所以例如你可能有一些像这样的代码(道歉这有点做作,只是假设BaseClass并且SecondClass正在进行非平凡的工作并包含许多方法,甚至根本不需要你定义):
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()
Run Code Online (Sandbox Code Playgroud)
然后你可以编写一些像这样的测试:
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()
Run Code Online (Sandbox Code Playgroud)
因此,基类上存在的方法仍然可以作为可以与之交互的模拟,但是您的类本身不会成为模拟.
我一直小心翼翼地确保这在python2和python3中都有效.
这应该对你有用。
import mock
ClassMock = mock.MagicMock # <-- Note the removed brackets '()'
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='...'>
Run Code Online (Sandbox Code Playgroud)
你不应该像你那样传递类的实例。mock.MagicMock是一个类,所以你直接传递它。
In [2]: inspect.isclass(mock.MagicMock)
Out[2]: True
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8240 次 |
| 最近记录: |