我有一个扩展unittest.TestCase的基类,我想修补那个基类,这样扩展这个基类的类也会应用补丁。
代码示例:
@patch("some.core.function", mocked_method)
class BaseTest(unittest.TestCase):
#methods
pass
class TestFunctions(BaseTest):
#methods
pass
修补 TestFunctions
class直接工作,但修补BaseTest类不会改变其功能 some.core.function
在 TestFunctions
。
您可能需要一个元类:元类只是定义了如何创建类。
默认情况下,所有类都是使用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)
这是无可争议的清洁。
一般来说,我更喜欢做这种事情 setUp
。您可以确保在测试完成后通过使用该补丁来清理补丁 tearDown
方法(或者,注册补丁的方法) stop
方法用 addCleanup
):
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
方法,它应该工作得很好。