最近在用Python自带的unittest框架做测试,但发现此包不支持类似JUnit那样的参数化测试功能。下面给出一简单实现,就是效率和代码美观程度差了点:)
参数化case的名字必须以 "parameterized_" 为前缀,后面跟真正的test名字;数据提供函数必须是classmethod,以 "collection_" 为前缀,后面跟真正的test名字。
比如 parameterized_test_add 和 collection_test_add 就是一组参数化case,其中testcase基础名字为test_add,参数化后具体的case为test_add_0, test_add_1, test_add_2 等等。关于parameterized_test_add 和 collection_test_add 的格式见下文。为实现此功能,必须重载unittest的 TestCase 和 TestLoader。
class Test(unittest.TestCase):
def __init__(self, methodName='runTest'):
def isParameterizedMethod(attrname):
return attrname.startswith("parameterized") and \
hasattr(getattr(self, attrname), '__call__')
testFnNames = filter(isParameterizedMethod, dir(self))
for func in testFnNames:
name = func.split("_", 1)[1]
collect = "collection_" + name
if hasattr(getattr(self, collect), '__call__'):
collectFunc = getattr(self, collect)
array = collectFunc()
for index in xrange(len(array)):
test = "%s_%d" % (name, index)
setattr(self.__class__, test, getattr(self, func)(array[index]))
# must called at last
unittest.TestCase.__init__(self, methodName)
class Loader(unittest.TestLoader):
def getTestCaseNames(self, testCaseClass):
"""Return a sorted sequence of method names found within testCaseClass
"""
testFnNames = unittest.TestLoader.getTestCaseNames(self, testCaseClass)
def isParameterizedMethod(attrname, testCaseClass=testCaseClass,
prefix="parameterized"):
return attrname.startswith(prefix) and \
hasattr(getattr(testCaseClass, attrname), '__call__')
testFnNames0 = filter(isParameterizedMethod, dir(testCaseClass))
for func in testFnNames0:
name = func.split("_", 1)[1]
collect = "collection_" + name
if hasattr(getattr(testCaseClass, collect), '__call__'):
collectFunc = getattr(testCaseClass, collect)
for item in xrange(len(collectFunc())):
testFnNames.append("%s_%d" % (name, item))
if self.sortTestMethodsUsing:
testFnNames.sort(key=_CmpToKey(self.sortTestMethodsUsing))
return testFnNames
from unittest import *
from Test import *
from Loader import *
class TestFunctions(Test):
@classmethod
def collection_test_add(cls):
return [1,2,3,5,7,9]
def parameterized_test_add(self, x):
def test_body(self):
print(x * x)
return test_body
if __name__ == '__main__':
suite = Loader().loadTestsFromTestCase(TestFunctions)
runner = unittest.TextTestRunner()
rc = runner.run(suite)
print(rc)
输出如下:
D:\workspace>test.py
1
.4
.9
.25
.49
.81
.
----------------------------------------------------------------------
Ran 6 tests in 0.010s
OK
在该用例中,真正的testcase定义在test_body函数中。collection_test_add 必须是一个无参的classmethod,返回一个list;parameterized_test_add 必须为非 classmethod 的成员函数,接受一个入参,该入参为 collection_test_add 所返回的 list 的元素,显然,该 list 的元素可以是任意数据类型,可以是list,tuple,dict等等,这样在test_body内可以接收更加丰富的输入。
本例中,collection_test_add 所返回的 list 中有6个元素,依次生成 test_add_0, test_add_1, test_add_2, test_add_3, test_add_4, test_add_5 共六个具体的case。