模拟超类 __init__ 方法或超类作为一个整体进行测试
Mock superclass __init__ method or superclass as a whole for testing
我想测试一个我写的Pythonclass,是这样的:
from external_library import GenericClass
class SpecificClass(GenericClass):
def __init__(self, a, b, c):
super(SpecificClass, self).__init__(a, b)
self.c = c
def specific_method(self, d):
self.initialize()
return d + self.c + self.b + self.a.fetch()
GenericClass
由外部库定义 (external_library
):
class GenericClass(object):
def __init__(self, a, b):
self.a = a
self.b = b + "append"
def initialize(self):
# it might be an expensive operation
self.a.initialize()
def specific_method(self, d):
raise NotImplementedError
我应该如何测试specific_method
?除了 GenericClass.initialize
,我应该将 GenericClass
作为一个整体进行模拟,GenericClass.__init__
、super
还是 GenericClass.a
和 GenericClass.b
?还是我处理问题的方法完全错误?
请使用 mock
作为模拟库,使用 pytest
作为测试框架。该解决方案需要 Python2.6+ 兼容。
提前致谢。
从综合示例中猜出最佳方法有点困难。什么时候可以只使用所需的模拟,并且尽可能多地使用真实对象是最好的。但这是一个权衡游戏,当创建真实对象很困难并且依赖性很深时,扭曲的模拟可能是非常强大的工具。此外,模拟为您提供了很多测试可以发挥作用的感觉点。这是要付出代价的:有时通过使用模拟,您可以隐藏仅通过集成测试即可引发的错误。
恕我直言,对于您的情况,最好只是模拟 a
并设置 fetch
的 return 值。您还可以测试 a.initialize()
调用
如果你的目标只是模拟 a.initialize()
(当然 a.fetch()
我想没有 initialize()
就没有意义);最好的就是 patch
a
object.
无论如何,最后的测试是关于给超级class打补丁的。在您的情况下,这有点棘手,您必须同时修补 __init__
并注入 a
、b
属性。如果你担心 GenericClass
在 init 中做了什么你必须直接修补它 __init__
方法:修补 class 是没有用的,因为它的引用已经在 SpecificClass
定义中(它是由 super
而不是 GenericClass.__init__(...)
解决)。也许在真实的代码中你应该反对 a
和 b
的只读 属性:同样,如果你可以使用真实的对象并且尽可能少地修补 method/object 是最好的选择。我喜欢 mock
框架,但有时使用它并不是最好的选择。
还有一件事:我使用 patch.multiple
只是为了得到一个更紧凑的示例,但是通过两个 patch.object
完成相同的工作来修补该方法。
import unittest
from mock import Mock, patch, DEFAULT, PropertyMock
from specific import SpecificClass
class A():
def initialize(self):
raise Exception("Too slow for tests!!!!")
def fetch(self):
return "INVALID"
class MyTestCase(unittest.TestCase):
def test_specific_method(self):
a = A()
with patch.multiple(a, initialize=DEFAULT, fetch=DEFAULT) as mocks:
mock_initialize, mock_fetch = mocks["initialize"], mocks["fetch"]
mock_fetch.return_value = "a"
sc = SpecificClass(a, "b", "c")
self.assertEqual("dcbappenda", sc.specific_method("d"))
mock_initialize.assert_called_with()
def test_sanity_check(self):
a = A()
sc = SpecificClass(a, "b", "c")
self.assertRaises(Exception, sc.specific_method, "d")
#You can patch it in your module if it is imported by from .. as ..
@patch("specific.GenericClass.__init__", autospec=True, return_value=None)
def test_super_class(self, mock_init):
sc = SpecificClass("a", "b", "c")
mock_init.assert_called_with(sc, "a", "b")
#Inject a and b
sc.a = Mock()
sc.b = "b"
#configure a
sc.a.fetch.return_value = "a"
self.assertEqual("dcba", sc.specific_method("d"))
sc.a.initialize.assert_called_with()
我想测试一个我写的Pythonclass,是这样的:
from external_library import GenericClass
class SpecificClass(GenericClass):
def __init__(self, a, b, c):
super(SpecificClass, self).__init__(a, b)
self.c = c
def specific_method(self, d):
self.initialize()
return d + self.c + self.b + self.a.fetch()
GenericClass
由外部库定义 (external_library
):
class GenericClass(object):
def __init__(self, a, b):
self.a = a
self.b = b + "append"
def initialize(self):
# it might be an expensive operation
self.a.initialize()
def specific_method(self, d):
raise NotImplementedError
我应该如何测试specific_method
?除了 GenericClass.initialize
,我应该将 GenericClass
作为一个整体进行模拟,GenericClass.__init__
、super
还是 GenericClass.a
和 GenericClass.b
?还是我处理问题的方法完全错误?
请使用 mock
作为模拟库,使用 pytest
作为测试框架。该解决方案需要 Python2.6+ 兼容。
提前致谢。
从综合示例中猜出最佳方法有点困难。什么时候可以只使用所需的模拟,并且尽可能多地使用真实对象是最好的。但这是一个权衡游戏,当创建真实对象很困难并且依赖性很深时,扭曲的模拟可能是非常强大的工具。此外,模拟为您提供了很多测试可以发挥作用的感觉点。这是要付出代价的:有时通过使用模拟,您可以隐藏仅通过集成测试即可引发的错误。
恕我直言,对于您的情况,最好只是模拟 a
并设置 fetch
的 return 值。您还可以测试 a.initialize()
调用
如果你的目标只是模拟 a.initialize()
(当然 a.fetch()
我想没有 initialize()
就没有意义);最好的就是 patch
a
object.
无论如何,最后的测试是关于给超级class打补丁的。在您的情况下,这有点棘手,您必须同时修补 __init__
并注入 a
、b
属性。如果你担心 GenericClass
在 init 中做了什么你必须直接修补它 __init__
方法:修补 class 是没有用的,因为它的引用已经在 SpecificClass
定义中(它是由 super
而不是 GenericClass.__init__(...)
解决)。也许在真实的代码中你应该反对 a
和 b
的只读 属性:同样,如果你可以使用真实的对象并且尽可能少地修补 method/object 是最好的选择。我喜欢 mock
框架,但有时使用它并不是最好的选择。
还有一件事:我使用 patch.multiple
只是为了得到一个更紧凑的示例,但是通过两个 patch.object
完成相同的工作来修补该方法。
import unittest
from mock import Mock, patch, DEFAULT, PropertyMock
from specific import SpecificClass
class A():
def initialize(self):
raise Exception("Too slow for tests!!!!")
def fetch(self):
return "INVALID"
class MyTestCase(unittest.TestCase):
def test_specific_method(self):
a = A()
with patch.multiple(a, initialize=DEFAULT, fetch=DEFAULT) as mocks:
mock_initialize, mock_fetch = mocks["initialize"], mocks["fetch"]
mock_fetch.return_value = "a"
sc = SpecificClass(a, "b", "c")
self.assertEqual("dcbappenda", sc.specific_method("d"))
mock_initialize.assert_called_with()
def test_sanity_check(self):
a = A()
sc = SpecificClass(a, "b", "c")
self.assertRaises(Exception, sc.specific_method, "d")
#You can patch it in your module if it is imported by from .. as ..
@patch("specific.GenericClass.__init__", autospec=True, return_value=None)
def test_super_class(self, mock_init):
sc = SpecificClass("a", "b", "c")
mock_init.assert_called_with(sc, "a", "b")
#Inject a and b
sc.a = Mock()
sc.b = "b"
#configure a
sc.a.fetch.return_value = "a"
self.assertEqual("dcba", sc.specific_method("d"))
sc.a.initialize.assert_called_with()