Python的unittest做引數化測試

hqzxsc2006發表於2015-04-03

約定

引數化case的名字必須以 "param_" 為字首,後面跟真正的test名字;資料提供函式必須是classmethod,以 "collection_" 為字首,後面跟真正的test名字。

比如 parameterized_test_add 和 collection_test_add 就是一組引數化case,其中testcase基礎名字為test_add,引數化後具體的case為test_add_0, test_add_1, test_add_2 等等。為實現此功能,必須過載unittest的 TestCase 和 TestLoader。

過載unittest.TestCase

class Test(unittest.TestCase):

    def __init__(self, methodName='runTest'):
        def isParameterizedMethod(attrname):
            return attrname.startswith("param") 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)


過載unittest.TestLoader

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]

    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)


在該用例中,真正的testcase定義在test_body函式中。collection_test_add 必須是一個無參的classmethod,返回一個list;parame_test_add 必須為非 classmethod 的成員函式,接受一個入參,該入參為 collection_test_add 所返回的 list 的元素,顯然,該 list 的元素可以是任意資料型別,可以是list,tuple,dict等等,這樣在test_body內可以接收更加豐富的輸入。

本例中,collection_test_add 所返回的 list 中有4個元素,依次生成 test_add_0, test_add_1, test_add_2, test_add_3共4個具體的case。

相關文章