sih*_*hrc 12 python python-2.7 python-mock python-unittest
我有一个扩展unittest.TestCase的基类,我想修补那个基类,这样扩展这个基类的类也会应用补丁.
代码示例:
@patch("some.core.function", mocked_method)
class BaseTest(unittest.TestCase):
#methods
pass
class TestFunctions(BaseTest):
#methods
pass
Run Code Online (Sandbox Code Playgroud)
修补TestFunctions该类直接工作,但修补BaseTest类不会改变some.core.functionin 的功能TestFunctions.
一般来说,我更喜欢做这种事情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
Run Code Online (Sandbox Code Playgroud)
如果你足够自律以便总是调用super你被覆盖的setUp方法,它应该可以正常工作.
您可能需要一个元类:元类只是定义了如何创建类.默认情况下,所有类都是使用Python的内置类创建的type:
>>> class Foo:
... pass
...
>>> type(Foo)
<class 'type'>
>>> isinstance(Foo, type)
True
Run Code Online (Sandbox Code Playgroud)
所以类实际上是实例type.现在,我们可以type创建一个自定义元类(一个创建类的类)的子类:
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
Run Code Online (Sandbox Code Playgroud)
我们需要控制类的创建,所以我们想要覆盖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
Run Code Online (Sandbox Code Playgroud)
现在您只需使用__metaclass__ = PatchMeta以下方法设置元类:
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
# methods
Run Code Online (Sandbox Code Playgroud)
问题是这一行:
cls = patch("some.core.function", mocked_method)(cls)
Run Code Online (Sandbox Code Playgroud)
所以目前我们始终与论据装饰"some.core.function"和mocked_method.相反,你可以使它使用类的属性,如下所示:
cls = patch(*cls.patch_args)(cls)
Run Code Online (Sandbox Code Playgroud)
然后添加patch_args到您的课程:
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
patch_args = ("some.core.function", mocked_method)
Run Code Online (Sandbox Code Playgroud)
编辑:正如评论中提到的@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)
Run Code Online (Sandbox Code Playgroud)
这是无可争议的清洁.
| 归档时间: |
|
| 查看次数: |
682 次 |
| 最近记录: |