在编写 Python 测试用例时,需要修补(patch)中间件中的一些函数以模拟某些行为。如果使用 Foord 的 mock 库进行修补,通常 需要为要修补的每个函数单独使用 @patch
装饰器进行修补。这会给测试用例代码带来很多重复的工作。
例如,以下是一个测试用例类,它继承自 TestCase
和 TestFreshProvisionedEachTest
类:
class MyTestCase(TestCase, TestFreshProvisionedEachTest):
def test_something(self):
# ...
def test_another_thing(self):
# ...
要修补中间件中的两个函数 extract_host_name
和 extract_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
类继承自 PatchedTestCase
和 TestFreshProvisionedEachTest
类。
在 TestMeta
类中,__new__()
方法是一个特殊的方法,它用于创建类实例。在 __new__()
方法中,首先获取需要修补的测试方法列表,然后遍历该列表,并为每个测试方法调用 patch_method()
方法进行修补。
在 PatchedTestCase
类中,patch_method()
方法用于修补测试方法。该方法使用 patch()
装饰器分别修补 extract_host_name
和 extract_host_key
两个函数。
使用这种方式,所有继承自 PatchedTestCase
的测试类的方法都会被修补,而无需为每个测试类单独进行修补。当有新的函数需要修补时,只需要修改 PatchedTestCase
类中的 patch_method()
方法即可。