我有一个扩展unittest.TestCase的基类,我想修补那个基类,这样扩展这个基类的类也会应用补丁。
代码示例:
@patch("some.core.function", mocked_method)
class BaseTest(unittest.TestCase):
#methods
pass
class TestFunctions(BaseTest):
#methods
pass
直接修补TestFunctions
类,但修补BaseTest类不会改变some.core.function
中TestFunctions
的功能。
答案 0 :(得分:7)
一般来说,我更喜欢在setUp
中做这类事情。通过使用tearDown
方法(或者使用addCleanup
注册补丁的stop
方法),您可以确保在测试完成后清理补丁:
class BaseTest(unittest.TestCase):
def setUp(self):
super(BaseTest, self).setUp()
my_patch = patch("some.core.function", mocked_method)
my_patch.start()
self.addCleanup(my_patch.stop)
class TestFunctions(BaseTest):
#methods
pass
如果你足够自律,总是在被覆盖的super
方法中调用setUp
,那么它应该可以正常工作。
答案 1 :(得分:7)
您可能需要一个元类:元类只是定义了如何创建类。
默认情况下,所有类都是使用Python的内置类type
:
>>> class Foo:
... pass
...
>>> type(Foo)
<class 'type'>
>>> isinstance(Foo, type)
True
所以类实际上是type
的实例。
现在,我们可以继承type
来创建自定义元类(一个创建类的类):
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
我们需要控制类的创建,所以我们想在这里覆盖type.__new__
,并在所有新实例上使用patch
装饰器:
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
def __new__(meta, name, bases, attrs):
cls = type.__new__(meta, name, bases, attrs)
cls = patch("some.core.function", mocked_method)(cls)
return cls
现在您只需使用__metaclass__ = PatchMeta
设置元类:
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
# methods
问题是这一行:
cls = patch("some.core.function", mocked_method)(cls)
所以目前我们总是用参数"some.core.function"
和mocked_method
进行装饰。
相反,你可以使它使用类的属性,如下所示:
cls = patch(*cls.patch_args)(cls)
然后将patch_args
添加到您的课程中:
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
patch_args = ("some.core.function", mocked_method)
编辑:正如@mgilson在评论中提到的,patch()
修改了类的方法,而不是返回一个新类。因此,我们可以将此__new__
替换为__init__
:
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
def __init__(cls, *args, **kwargs):
super(PatchMeta, self).__init__(*args, **kwargs)
patch(*cls.patch_args)(cls)
这无可争议地更清洁。