本文想针对测验中一种很常见的测验场景,即参数化测验,持续聊聊关于测验的论题,并测验将这几个测验结构串联起来,做一个横向的比对,加深了解。
1、什么是参数化测验?
关于一般测验来说,一个测验办法只需求运转一遍,而参数化测验关于一个测验办法,或许需求传入一系列参数,然后进行屡次测验。
比方,咱们要测验某个体系的登录功用,就或许要别离传入不同的用户名与暗码,进行测验:运用包括不合法字符的用户名、运用未注册的用户名、运用超长的用户名、运用过错的暗码、运用合理的数据等等。
参数化测验是一种“数据驱动测验”(Data-DrivenTest),在同一个办法上测验不同的参数,以掩盖一切或许的预期分支的成果。它的测验数据能够与测验行为别离,被放入文件、数据库或许外部介质中,再由测验程序读取。
2、参数化测验的完成思路?
一般而言,一个测验办法便是一个最小的测验单元,其功用应该尽量地原子化和单一化。
先来看看两种完成参数化测验的思路:一种是写一个测验办法,在其内部对一切测验参数进行遍历;另一种是在测验办法之外写遍历参数的逻辑,然后顺次调用该测验办法。
这两种思路都能到达测验意图,在简略事务中,没有缺点。可是,实际上它们都只要一个测验单元,在计算测验用例数状况,或许生成测验报告的时分,并不达观。可扩展性也是个问题。
那么,现有的测验结构是怎么处理这个问题的呢?
它们都凭借了装修器,首要的思路是:使用原测验办法(例如test()),来生成多个新的测验办法(例如test1()、test2()……),并将参数顺次赋值给它们。
因为测验结构们一般把一个测验单元计算为一个“test”,所以这种“由终身多”的思路比较前面的两种思路,在计算测验成果时,就具有很大的优势。
3、参数化测验的运用办法?
Python规范库中的unittest本身不支持参数化测验,为了处理这个问题,有人专门开发了两个库:一个是ddt,一个是parameterize。
ddt正好是“Data-DrivenTests”(数据驱动测验)的缩写。典型用法:
importunittestfromddTImportddt,data,unpack@ddtclassMyTest(unittest.TestCase):@data((3,1),(-1,0),(1.2,1.0))@unpackdeftest_values(self,first,second):self.assertTrue(first>second)unittest.main(verbosity=2)
运转的成果如下:
test_values_1__3__1_(__main__.MyTest)...oktest_values_2___1__0_(__main__.MyTest)...FAILtest_values_3__1_2__1_0_(__main__.MyTest)...ok==================================================FAIL:test_values_2___1__0_(__main__.MyTest)--------------------------------------------------Traceback(mostrecentcalllast):File"C:/Python36/lib/site-packages/ddt.py",line145,inwrapperreturnfunc(self,*args,**kwargs)File"C:/Users/pythoncat/PycharmProjects/study/testparam.py",line9,intest_valuesself.assertTrue(first>second)AsserTIonError:Falseisnottrue----------------------------------------------Ran3testsin0.001sFAILED(failures=1)
成果显现有3个tests,并详细展现了运转状况以及断语失利的信息。
需求留意的是,这3个test别离有一个姓名,姓名中还携带了其参数的信息,而本来的test_values办法则不见了,现已被一拆为三。
在上述比如中,ddt库运用了三个装修器(@ddt、@data、@unpack),实在是很丑恶。下面看看相对更好用的parameterized库:
importunittestfromparameterizedimportparameterizedclassMyTest(unittest.TestCase):@parameterized.expand([(3,1),(-1,0),(1.5,1.0)])deftest_values(self,first,second):self.assertTrue(first>second)unittest.main(verbosity=2)
测验成果如下:
test_values_0(__main__.MyTest)...oktest_values_1(__main__.MyTest)...FAILtest_values_2(__main__.MyTest)...ok=========================================FAIL:test_values_1(__main__.MyTest)-----------------------------------------Traceback(mostrecentcalllast):File"C:/Python36/lib/site-packages/parameterized/parameterized.py",line518,instandalone_funcreturnfunc(*(a+p.args),**p.kwargs)File"C:/Users/pythoncat/PycharmProjects/study/testparam.py",line7,intest_valuesself.assertTrue(first>second)AsserTIonError:Falseisnottrue----------------------------------------Ran3testsin0.000sFAILED(failures=1)
这个库只用了一个装修器@parameterized.expand,写法上可就清新多了。
相同提示下,本来的测验办法现已消失了,取而代之的是三个新的测验办法,仅仅新办法的命名规矩与ddt的比如不同算了。
介绍完unittest,接着看现已死翘翘了的nose以及重生的nose2。nose系结构是带了插件(plugins)的unittest,以上的用法是相通的。
别的,nose2中还供给了自带的参数化完成:
importunittestfromnose2.toolsimportparams@params(1,2,3)deftest_nums(num):assertnum< 4 class Test(unittest.TestCase): @params((1, 2), (2, 3), (4, 5)) def test_less_than(self, a, b): assert a < b
最终,再来看下pytest结构,它这样完成参数化测验:
importpytest@pytest.mark.parametrize("first,second",[(3,1),(-1,0),(1.5,1.0)])deftest_values(first,second):assert(first>second)
测验成果如下:
====================testsessionstarts====================platformwin32--Python3.6.1,pytest-5.3.1,py-1.8.0,pluggy-0.13.1rootdir:C:/Users/pythoncat/PycharmProjects/studycollected3itemstestparam.py.Ftestparam.py:3(test_values[-1-0])first=-1,second=0@pytest.mark.parametrize("first,second",[(3,1),(-1,0),(1.5,1.0)])deftest_values(first,second):>assert(first>second)Eassert-1>0testparam.py:6:AssertionError.[100%]=========================FAILURES==========================_________________________test_values[-1-0]_________________________first=-1,second=0@pytest.mark.parametrize("first,second",[(3,1),(-1,0),(1.5,1.0)])deftest_values(first,second):>assert(first>second)Eassert-1>0testparam.py:6:AsserTIonError=====================1failed,2passedin0.08s=====================Processfinishedwithexitcode0
仍然要提示大伙留意,pytest也做到了由一变三,可是咱们却看不到有新命名的办法的信息。这是否意味着它并没有发生新的测验办法呢?或许仅仅是把新办法的信息躲藏起来了?
4、最终小结
上文中介绍了参数化测验的概念、完成思路,以及在三个干流的Python测验结构中的运用办法。我只用了最简略的比如,为的是快速科普(言多必失)。
可是,这个论题其实还没有完毕。关于咱们说到的几个能完成参数化的库,抛去写法上迥然不同的差异,它们在详细代码层面上,又会有什么样的差异呢?
详细来说,它们是怎么做到把一个办法变成多个办法,而且将每个办法与相应的参数绑定起来的呢?在完成中,需求处理哪些扎手的问题?
在剖析一些源码的时分,我发现这个论题还挺有意思,所以预备别的写一篇文章。那么,本文就到此为止了,谢谢阅览。
作者简介:豌豆花下猫,生于广东结业于武大,现为苏漂程序员,有一些极客思想,也有一些人文情怀,有一些温度,还有一些情绪。