继承补丁 class
Inheriting a patched class
我有一个基础 class 扩展 unittest.TestCase,我想修补那个基础 class,这样 class 就可以扩展这个基础 class也会应用补丁。
代码示例:
@patch("some.core.function", mocked_method)
class BaseTest(unittest.TestCase):
#methods
pass
class TestFunctions(BaseTest):
#methods
pass
直接给 TestFunctions
class 打补丁,但是给 BaseTest class 打补丁不会改变 TestFunctions
中 some.core.function
的功能。
一般来说,我更喜欢在 setUp
. You can make sure that the patch gets cleaned up after the test is completed by making use of the tearDown
method (or alternatively, registering a the patch's stop
method with 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
只要您有足够的纪律性,总是在覆盖的 setUp
方法中调用 super
,它应该可以正常工作。
您可能需要一个 metaclass:metaclass 简单地定义了 class 的创建方式。
默认情况下,所有 classes 都是使用 Python 的内置 class type
:
创建的
>>> class Foo:
... pass
...
>>> type(Foo)
<class 'type'>
>>> isinstance(Foo, type)
True
所以 classes 实际上是 type
的实例。
现在,我们可以通过 subclass type
创建一个自定义元 class (一个 class 创建 classes):
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
我们需要控制 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
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
# methods
问题出在这一行:
cls = patch("some.core.function", mocked_method)(cls)
所以目前我们总是用参数"some.core.function"
和mocked_method
装饰。
相反,你可以让它使用 class 的属性,像这样:
cls = patch(*cls.patch_args)(cls)
然后将 patch_args
添加到您的 classes:
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
patch_args = ("some.core.function", mocked_method)
编辑: 正如@mgilson 在评论中提到的,patch()
修改了 class 的方法,而不是返回一个新的 class。因此,我们可以用 __init__
:
替换 __new__
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)
这无疑是更干净的。
我有一个基础 class 扩展 unittest.TestCase,我想修补那个基础 class,这样 class 就可以扩展这个基础 class也会应用补丁。
代码示例:
@patch("some.core.function", mocked_method)
class BaseTest(unittest.TestCase):
#methods
pass
class TestFunctions(BaseTest):
#methods
pass
直接给 TestFunctions
class 打补丁,但是给 BaseTest class 打补丁不会改变 TestFunctions
中 some.core.function
的功能。
一般来说,我更喜欢在 setUp
. You can make sure that the patch gets cleaned up after the test is completed by making use of the tearDown
method (or alternatively, registering a the patch's stop
method with 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
只要您有足够的纪律性,总是在覆盖的 setUp
方法中调用 super
,它应该可以正常工作。
您可能需要一个 metaclass:metaclass 简单地定义了 class 的创建方式。
默认情况下,所有 classes 都是使用 Python 的内置 class type
:
>>> class Foo:
... pass
...
>>> type(Foo)
<class 'type'>
>>> isinstance(Foo, type)
True
所以 classes 实际上是 type
的实例。
现在,我们可以通过 subclass type
创建一个自定义元 class (一个 class 创建 classes):
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
我们需要控制 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
装饰。
相反,你可以让它使用 class 的属性,像这样:
cls = patch(*cls.patch_args)(cls)
然后将 patch_args
添加到您的 classes:
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
patch_args = ("some.core.function", mocked_method)
编辑: 正如@mgilson 在评论中提到的,patch()
修改了 class 的方法,而不是返回一个新的 class。因此,我们可以用 __init__
:
__new__
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)
这无疑是更干净的。