作者:豌豆花下猫
来源:Python猫
在之前的文章《python中如何实现参数化测试?》中,我提到了在python中实现参数化测试的几个库,并留下一个问题:
它们是如何做到把一个方法变成多个方法,并且将每个方法与相应的参数绑定起来的呢?
我们再提炼一下,原问题等于是:在一个类中,如何使用装饰器把一个类方法变成多个类方法(或者产生类似的效果)?
#带有一个方法的测试类
classTestClass:
deftest_func(self):
pass
#使用装饰器,生成多个类方法
classTestClass:
deftest_func1(self):
pass
deftest_func2(self):
pass
deftest_func3(self):
pass
Python中装饰器的本质就是移花接木,用一个新的方法来替代被装饰的方法。在实现参数化的过程中,我们介绍过的几个库到底用了什么手段/秘密武器呢?
1、ddt如何实现参数化?
先回顾一下上篇文章中ddt库的写法:
importunittest
fromddtimportddt,data,unpack
ddtclassMyTest(unittest.TestCase):
data((3,1),(-1,0),(1.2,1.0))unpackdeftest(self,first,second):
pass
ddt可提供4个装饰器:1个加在类上的
ddt,还有3个加在类方法上的data、unpack和file_data(前文未提及)。先看看加在类方法上的三个装饰器的作用:
#ddt版本(win):1.2.1
defdata(*values):
globalindex_len
index_len=len(str(len(values)))
returnidata(values)
defidata(iterable):
defwrapper(func):
setattr(func,DATA_ATTR,iterable)
returnfunc
returnwrapper
defunpack(func):
setattr(func,UNPACK_ATTR,True)
returnfunc
deffile_data(value):
defwrapper(func):
setattr(func,FILE_ATTR,value)
returnfunc
returnwrapper
它们的共同作用是在类方法上setattr()添加属性。至于这些属性在什么时候使用?下面看看加在类上的
ddt装饰器源码:第一层for循环遍历了所有的类方法,然后是if/elif两条分支,分别对应DATA_ATTR/FILE_ATTR,即对应参数的两种来源:数据(
data)和文件(file_data)。elif分支有解析文件的逻辑,之后跟处理数据相似,所以我们把它略过,主要看前面的if分支。这部分的逻辑很清晰,主要完成的任务如下:
遍历类方法的参数键值对根据原方法及参数对,创建新的方法名获取原方法的文档字符串对元组和列表类型的参数作在测试类上添加新的测试方法,并绑定参数与文档字符串
分析源码,可以看出,
data、unpack和file_data这三个装饰器主要是设置属性并传参,而ddt装饰器才是核心的处理逻辑。这种将装饰器分散(分别加在类与类方法上),再组合使用的方案,很不优雅。为什么就不能统一起来使用呢?后面我们会分析它的难言之隐,先按下不表,看看其它的实现方案是怎样的?
2、parameterized如何实现参数化?
先回顾一下上篇文章中parameterized库的写法:
importunittest
fromparameterizedimportparameterized
classMyTest(unittest.TestCase):
parameterized.expand([(3,1),(-1,0),(1.5,1.0)])deftest_values(self,first,second):
self.assertTrue(firstsecond)
它提供了一个装饰器类
parameterized,源码如下(版本0.7.1),主要做了一些初始的校验和参数解析,并非我们