简体   繁体   English

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

[英]How to run unittest test cases in the order they are declared

I fully realize that the order of unit tests should not matter.我完全意识到单元测试的顺序应该无关紧要。 But these unit tests are as much for instructional use as for actual unit testing, so I would like the test output to match up with the test case source code.但是这些单元测试与实际单元测试一样多用于教学,所以我希望测试输出与测试用例源代码相匹配。

I see that there is a way to set the sort order by setting the sortTestMethodsUsing attribute on the test loader.我看到有一种方法可以通过在测试加载器上设置sortTestMethodsUsing属性来设置排序顺序。 The default is a simple cmp() call to lexically compare names.默认值是一个简单的cmp()调用,用于词法比较名称。 So I tried writing a cmp -like function that would take two names, find their declaration line numbers and them return the cmp() -equivalent of them:因此,我尝试编写一个类似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()

When I run this, I get this output:当我运行这个时,我得到这个输出:

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

OK

indicating that the test cases are not running in the declaration order.表明测试用例没有按照声明顺序运行。

My sort function is just not being called, so I suspect that main() is building a new test loader, which is wiping out my sort function.我的排序函数没有被调用,所以我怀疑 main() 正在构建一个新的测试加载器,它正在清除我的排序函数。

The solution is to create a TestSuite explicitly, instead of letting unittest.main() follow all its default test discovery and ordering behavior.解决方案是显式创建一个 TestSuite,而不是让 unittest.main() 遵循其所有默认的测试发现和排序行为。 Here's how I got it to work:这是我如何让它工作的:

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)

This gives the desired output:这给出了所需的输出:

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

OK

It is important to note that the test method in each class must be named runTest .需要注意的是,每个类中的测试方法必须命名为runTest

You can manually build a TestSuite where your TestCases and all tests inside them run by line number:您可以手动构建一个 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())

Output:输出:

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

As stated in the name, sortTestMethodsUsing is used to sort test methods .顾名思义, sortTestMethodsUsing用于对测试方法进行排序。 It is not used to sort classes.它不用于对类进行排序。 (It is not used to sort methods in different classes either; separate classes are handled separately.) (它也不用于对不同类中的方法进行排序;单独的类是单独处理的。)

If you had two test methods in the same class, sortTestMethodsUsing would be used to determine their order.如果您在同一个类中有两个测试方法, sortTestMethodsUsing将用于确定它们的顺序。 (At that point, you would get an exception because your function expects class names.) (此时,您会得到一个异常,因为您的函数需要类名。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM