通过元类在 Python 中修补子类的所有方法

在编写 Python 测试用例时,需要修补(patch)中间件中的一些函数以模拟某些行为。如果使用 Foord 的 mock 库进行修补,通常 需要为要修补的每个函数单独使用 @patch 装饰器进行修补。这会给测试用例代码带来很多重复的工作。

例如,以下是一个测试用例类,它继承自 TestCaseTestFreshProvisionedEachTest 类:

class MyTestCase(TestCase, TestFreshProvisionedEachTest):

    def test_something(self):
        # ...

    def test_another_thing(self):
        # ...

要修补中间件中的两个函数 extract_host_nameextract_host_key,需要在类定义之前使用 @patch 装饰器进行修补:

@patch('spotlight.middleware.extract_host_name', new=lambda host_name: TEST_DOMAIN)
@patch('spotlight.middleware.extract_host_key', new=lambda host_key: company_name_to_db(TEST_DOMAIN))
class MyTestCase(TestCase, TestFreshProvisionedEachTest):

    def test_something(self):
        # ...

    def test_another_thing(self):
        # ...

这种方式的问题是,它需要为每个测试类单独进行修补,并且当有新的函数需要修补时,需要修改所有测试类。

解决方案

一种解决方法是使用元类来修补子类的所有方法。元类是类的类,它可以控制类的创建过程,并可以为类添加额外的功能。

以下是一个使用元类进行修补的示例:

class TestMeta(type(TestCase)):
    def patch_method(cls, meth):
        raise NotImplementedError

    def __new__(mcls, name, bases, dct):
        to_patch = []

        for methname, meth in dct.items():
            if methname.startswith('test') and callable(meth):
                to_patch.append(methname)

        cls = super().__new__(mcls, name, bases, dct)

        for methname in to_patch:
            meth = getattr(cls, methname)
            meth = cls.patch_method(meth)
            setattr(cls, methname, meth)

        return cls

class PatchedTestCase(TestCase, metaclass=TestMeta):
    @classmethod
    def patch_method(cls, meth):
        meth = patch('spotlight.middleware.extract_host_name',
                     new=lambda host_name: TEST_DOMAIN)(meth)

        meth = patch('spotlight.middleware.extract_host_key',
                     new=lambda host_key: company_name_to_db(TEST_DOMAIN))(meth)

        return meth

class MyTestCase(PatchedTestCase, TestFreshProvisionedEachTest):

    def test_something(self):
        # ...

    def test_another_thing(self):
        # ...

在这个示例中,TestMeta 是一个元类,它继承自 type(TestCase)TestMeta 类定义了一个 patch_method() 方法,该方法用于修补测试方法。

PatchedTestCase 类继承自 TestCase,并使用 TestMeta 作为元类。PatchedTestCase 类定义了一个 patch_method() 类方法,该方法用于修补测试方法。

MyTestCase 类继承自 PatchedTestCaseTestFreshProvisionedEachTest 类。

TestMeta 类中,__new__() 方法是一个特殊的方法,它用于创建类实例。在 __new__() 方法中,首先获取需要修补的测试方法列表,然后遍历该列表,并为每个测试方法调用 patch_method() 方法进行修补。

PatchedTestCase 类中,patch_method() 方法用于修补测试方法。该方法使用 patch() 装饰器分别修补 extract_host_nameextract_host_key 两个函数。

使用这种方式,所有继承自 PatchedTestCase 的测试类的方法都会被修补,而无需为每个测试类单独进行修补。当有新的函数需要修补时,只需要修改 PatchedTestCase 类中的 patch_method() 方法即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值