如何按照声明的顺序运行单元测试用例

时间:2018-09-24 02:46:40

标签: python python-3.x python-unittest

我完全意识到单元测试的顺序无关紧要。但是这些单元测试在教学上和实际单元测试一样多,因此我希望测试输出与测试用例源代码相匹配。

我看到有一种方法可以通过在测试加载器上设置numba.prange属性来设置排序顺序。默认是一个简单的sortTestMethodsUsing调用,以词法比较名称。因此,我尝试编写一个类似cmp()的函数,该函数将使用两个名称,找到它们的声明行号,然后返回与它们等效的cmp

cmp()

运行此命令时,将得到以下输出:

import unittest

class TestCaseB(unittest.TestCase):
    def test(self):
        print("running test case B")

class TestCaseA(unittest.TestCase):
    def test(self):
        print("running test case A")

import inspect
def get_decl_line_no(cls_name):
    cls = globals()[cls_name]
    return inspect.getsourcelines(cls)[1]

def sgn(x):
    return -1 if x < 0 else 1 if x > 0 else 0

def cmp_class_names_by_decl_order(cls_a, cls_b):
    a = get_decl_line_no(cls_a)
    b = get_decl_line_no(cls_b)
    return sgn(a - b)

unittest.defaultTestLoader.sortTestMethodsUsing = cmp_class_names_by_decl_order
unittest.main()

指示测试用例未按声明顺序运行。

我的排序函数只是没有被调用,所以我怀疑main()正在构建一个新的测试加载器,这正在清除我的排序函数。

3 个答案:

答案 0 :(得分:2)

您可以手动构建TestSuite,其中TestCases和其中的所有测试均按行号运行:

# Python 3.8.3
import unittest
import sys
import inspect


def isTestClass(x):
    return inspect.isclass(x) and issubclass(x, unittest.TestCase)


def isTestFunction(x):
    return inspect.isfunction(x) and x.__name__.startswith("test")


class TestB(unittest.TestCase):
    def test_B(self):
        print("Running test_B")
        self.assertEqual((2+2), 4)

    def test_A(self):
        print("Running test_A")
        self.assertEqual((2+2), 4)

    def setUpClass():
        print("TestB Class Setup")


class TestA(unittest.TestCase):
    def test_A(self):
        print("Running test_A")
        self.assertEqual((2+2), 4)

    def test_B(self):
        print("Running test_B")
        self.assertEqual((2+2), 4)

    def setUpClass():
        print("TestA Class Setup")


def suite():

    # get current module object
    module = sys.modules[__name__]

    # get all test className,class tuples in current module
    testClasses = [
        tup for tup in
        inspect.getmembers(module, isTestClass)
    ]

    # sort classes by line number
    testClasses.sort(key=lambda t: inspect.getsourcelines(t[1])[1])

    testSuite = unittest.TestSuite()

    for testClass in testClasses:
        # get list of testFunctionName,testFunction tuples in current class
        classTests = [
            tup for tup in
            inspect.getmembers(testClass[1], isTestFunction)
        ]

        # sort TestFunctions by line number
        classTests.sort(key=lambda t: inspect.getsourcelines(t[1])[1])

        # create TestCase instances and add to testSuite;
        for test in classTests:
            testSuite.addTest(testClass[1](test[0]))

    return testSuite


if __name__ == '__main__':

    runner = unittest.TextTestRunner()
    runner.run(suite())

输出:

TestB Class Setup
Running test_B
.Running test_A
.TestA Class Setup
Running test_A
.Running test_B
.
----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

答案 1 :(得分:0)

如名称中所述,sortTestMethodsUsing用于对测试方法进行排序。它不用于对类进行排序。 (它也不用于对不同类中的方法进行排序;单独的类是分别处理的。)

如果同一类中有两个测试方法,则sortTestMethodsUsing将用于确定其顺序。 (这时,您会得到一个异常,因为您的函数需要类名。)

答案 2 :(得分:0)

解决方案是显式创建TestSuite,而不是让unittest.main()遵循其所有默认测试发现和排序行为。这是我的工作方式:

import unittest

class TestCaseB(unittest.TestCase):
    def runTest(self):
        print("running test case B")

class TestCaseA(unittest.TestCase):
    def runTest(self):
        print("running test case A")


import inspect
def get_decl_line_no(cls):
    return inspect.getsourcelines(cls)[1]

# get all test cases defined in this module
test_case_classes = list(filter(lambda c: c.__name__ in globals(), 
                                unittest.TestCase.__subclasses__()))

# sort them by decl line no
test_case_classes.sort(key=get_decl_line_no)

# make into a suite and run it
suite = unittest.TestSuite(cls() for cls in test_case_classes)
unittest.TextTestRunner().run(suite)

这将提供所需的输出:

running test case B
.running test case A
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

请注意,每个类中的测试方法必须命名为runTest