当前位置: 首页 > 后端技术 > Python

Python中如何实现参数化测试?_0

时间:2023-03-26 17:49:35 Python

之前翻到单元测试框架系列文章,介绍了unittest、nose/nose2和pytest这三个最流行的Python测试框架。本文想围绕测试中一个非常常见的测试场景,即参数化测试,继续聊测试这个话题,并尝试将这些测试框架串联起来,进行横向比较,加深理解。1.什么是参数测试?对于普通的测试,一个测试方法只需要运行一次,但是对于参数化测试,可能需要向一个测试方法中传入一系列的参数,然后进行多次测试。比如我们要测试某个系统的登录功能,可能需要传入不同的用户名和密码进行测试:使用含有非法字符的用户名,使用未注册的用户名,使用超长用户名,使用错误密码,使用敏感数据等。参数化测试是一种“数据驱动测试”,它在同一方法上测试不同的参数以覆盖所有可能的预期分支的结果。它的测试数据可以从测试行为中分离出来,放入文件、数据库或外部介质中,然后由测试程序读取。2、参数化测试的实现思路是什么?一般来说,一个测试方法是最小的测试单元,它的功能应该尽可能的原子化和简单化。下面我们来看两种实现参数化测试的方法:一种是写一个测试方法,遍历里面所有的测试参数;另一种是在测试方法外写遍历参数的逻辑,然后调用测试方法。这两种思路都可以达到测试的目的。在简单的业务中,它没有错。但实际上,它们都只有一个测试单元,无论是统计测试用例数量还是生成测试报告,都不容乐观。可扩展性也是一个问题。那么,现有的测试框架是如何解决这个问题的呢?它们都使用装饰器,主要思想是:利用原有的测试方法(如test())生成多个新的测试方法(如test1()、test2()...),并为它们分配参数。由于测试框架通常将一个测试单元算作一个“测试”,所以这种“不止一个生命周期”的思路相比前两种思路在统计测试结果上有很大的优势。3.如何使用参数化测试?Python标准库中的unittest本身不支持参数化测试。为了解决这个问题,有人专门开发了两个库:一个是ddt,一个是参数化的。ddt恰好是“数据驱动测试”的首字母缩写词。典型用法: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)...好的=====================================================失败:test_values_2___1__0_(__main__.MyTest)----------------------------------------------回溯(最近调用最后一次):文件“C:\Python36\lib\site-packages\ddt.py”,第145行,包装器返回func(self,*args,**kwargs)文件“C:/Users/pythoncat/PycharmProjects/study/testparam.py”,line9,intest_valuesself.assertTrue(first>second)AssertionError:Falseisnottrue------------------------------------------------跑3testsin0.001sFAILED(failures=1)result显示有3个测试,并显示run宁状态和断言失败信息的详细信息。需要注意的是,这三个测试每个都有一个名字,名字中还携带了其参数的信息,而原来的test_values方法没有了,被拆分成了三个。在上面的例子中,ddt库使用了三个装饰器(@ddt、@data、@unpack),实在是太丑了。我们来看看相对较好的参数化库: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=============================================失败:test_values_1(__main__.MyTest)--------------------------------------------回溯(最近调用最后):文件“C:\Python36\lib\site-packages\parameterized\parameterized.py”,第518行,在standalone_funcreturnfunc(*(a+p.args),**p.kwargs)File"C:/Users/pythoncat/PycharmProjects/study/testparam.py",line7,intest_valuesself.assertTrue(first>second)AssertionError:Falseisnottrue--------------------------------------在0.000秒内进行了3次测试失败(失败=1)这仅限图书馆使用装饰器@parameterized.exp而且,写法清爽多了。另外提醒一下原来的测试方法已经消失了,取而代之的是三个新的测试方法,但是新方法的命名规则和ddt的例子不一样。介绍完unittest,我们再看看deadnose和newnose2。基于nose的框架是unittestwithplugins,上面的用法类似。此外,nose2还提供了自己的参数化实现:importunittestfromnose2.toolsimportparams@params(1,2,3)deftest_nums(num):assertnum<4classTest(unittest.TestCase):@params((1,2),(2,3),(4,5))deftest_less_than(self,a,b):assertasecond)测试结果如下如下:=====================测试会话开始=====================平台win32--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:断言错误。[100%]=========================失败=============================______________________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=======================1个失败,2个在0.08秒内通过======================Processfinishedwithexitcode0还是要提醒大家,pytest也从一变成了三,但是没看到一个newnamingmethod是不是意味着它不会生成新的测试方法?或者它只是隐藏了有关新方法的信息?4.最后总结以上介绍了三种主流Python测试框架中参数化测试的概念、实现思路和使用方法。为了快速科学起见,我只使用了最简单的例子(更多的话会丢失)。然而,这个话题还没有结束。对于我们提到的几个可以实现参数化的库,抛开写法上的不同,在具体的代码层面会有什么样的不同呢?具体来说,他们是如何将一个方法变成多个方法,并为每个方法绑定相应的参数呢?在实施中,需要解决哪些棘手问题?在分析一些源码的时候,觉得这个话题挺有意思的,准备另写一篇。好了,本文就这些了,感谢阅读。公众号【Python猫】,本号连载系列精品文章,包括喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写作、优质英文推荐与翻译等,欢迎收看注意。