Python中怎么将一个类方法变为多个方法
导读:本文共3393字符,通常情况下阅读需要11分钟。同时您也可以点击右侧朗读,来听本文内容。按键盘←(左) →(右) 方向键可以翻页。
摘要: 1、ddt 如何实现参数化?先回顾一下上篇文章中 ddt 库的写法:importunittestfromddtimportddt,data,unpack@ddtclassMyTest(unittest.TestCase):@data((3,1),(-1,0),(1.2,1.0))@unpackdeftest(self,first,second):passddt ... ...
目录
(为您整理了一些要点),点击可以直达。1、ddt 如何实现参数化?
先回顾一下上篇文章中 ddt 库的写法:
importunittestfromddtimportddt,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.1defdata(*values):globalindex_lenindex_len=len(str(len(values)))returnidata(values)defidata(iterable):defwrapper(func):setattr(func,DATA_ATTR,iterable)returnfuncreturnwrapperdefunpack(func):setattr(func,UNPACK_ATTR,True)returnfuncdeffile_data(value):defwrapper(func):setattr(func,FILE_ATTR,value)returnfuncreturnwrapper
它们的共同作用是在类方法上 setattr() 添加属性。至于这些属性在什么时候使用?下面看看加在类上的 @ddt 装饰器源码:
第一层 for 循环遍历了所有的类方法,然后是 if/elif 两条分支,分别对应 DATA_ATTR/FILE_ATTR,即对应参数的两种来源:数据(@data)和文件(@file_data)。
elif 分支有解析文件的逻辑,之后跟处理数据相似,所以我们把它略过,主要看前面的 if 分支。这部分的逻辑很清晰,主要完成的任务如下:
遍历类方法的参数键值对
根据原方法及参数对,创建新的方法名
获取原方法的文档字符串
对元组和列表类型的参数作解包
在测试类上添加新的测试方法,并绑定参数与文档字符串
分析源码,可以看出,@data、@unpack 和 @file_data 这三个装饰器主要是设置属性并传参,而 @ddt 装饰器才是核心的处理逻辑。
这种将装饰器分散(分别加在类与类方法上),再组合使用的方案,很不优雅。为什么就不能统一起来使用呢?后面我们会分析它的难言之隐,先按下不表,看看其它的实现方案是怎样的?
2、parameterized 如何实现参数化?
先回顾一下上篇文章中 parameterized 库的写法:
importunittestfromparameterizedimportparameterizedclassMyTest(unittest.TestCase):@parameterized.expand([(3,1),(-1,0),(1.5,1.0)])deftest_values(self,first,second):self.assertTrue(first>second)
它提供了一个装饰器类 @parameterized,源码如下(版本 0.7.1),主要做了一些初始的校验和参数解析,并非我们关注的重点,略过。
我们主要关注这个装饰器类的 expand() 方法,它的文档注释中写到:
A"bruteforce"methodofparameterizingtestcases.Createsnewtestcasesandinjectsthemintothenamespacethatthewrappedfunctionisbeingdefinedin.Usefulforparameterizingtestsinsubclassesof'UnitTest',whereNosetestgeneratorsdon'twork.
关键的两个动作是:“creates new test cases(创建新的测试单元)”和“inject them into the namespace…(注入到原方法的命名空间)”。
关于第一点,它跟 ddt 是相似的,只是一些命名风格上的差异,以及参数的解析及绑定不同,不值得太关注。
最不同的则是,怎么令新的测试方法生效?
parameterized 使用的是一种“注入”的方式:
inspect 是个功能强大的标准库,在此用于获取程序调用栈的信息。前三句代码的目的是取出 f_locals,它的含义是“local namespace seen by this frame”,此处 f_locals 指的就是类的局部命名空间。
说到局部命名空间,你可能会想到 locals(),但是,我们之前有文章提到过“locals() 与 globals() 的读写问题”,locals() 是可读不可写的,所以这段代码才用了 f_locals。
3、pytest 如何实现参数化?
按惯例先看看上篇文章中的写法:
importpytest@pytest.mark.parametrize("first,second",[(3,1),(-1,0),(1.5,1.0)])deftest_values(first,second):assert(first>second)
首先看到“mark”,pytest 里内置了一些标签,例如 parametrize、timeout、skipif、xfail、tryfirst、trylast 等,还支持用户自定义的标签,可以设置执行条件、分组筛选执行,以及修改原测试行为等等。
用法也是非常简单的,然而,其源码可复杂多了。我们这里只关注 parametrize,先看看核心的一段代码:
根据传入的参数对,它复制了原测试方法的调用信息,存入待调用的列表里。跟前面分析的两个库不同,它并没有在此创建新的测试方法,而是复用了已有的方法。在 parametrize() 所属的 Metafunc 类往上查找,可以追踪到 _calls 列表的使用位置:
最终是在 Function 类中执行:
好玩的是,在这里我们可以看到几行神注释……
</div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
Python中怎么将一个类方法变为多个方法的详细内容,希望对您有所帮助,信息来源于网络。