如何 运行 按声明顺序对测试用例进行单元测试
How to run unittest test cases in the order they are declared
我完全意识到单元测试的顺序应该无关紧要。但是这些单元测试既用于实际单元测试也用于教学,所以我希望测试输出与测试用例源代码相匹配。
我看到有一种方法可以通过在测试加载器上设置 sortTestMethodsUsing
属性来设置排序顺序。默认是一个简单的 cmp()
调用来比较名称。因此,我尝试编写一个类似于 cmp
的函数,该函数将采用两个名称,找到它们的声明行号和它们 return 它们的 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()
当我运行这个时,我得到这个输出:
running test case A
.running test case B
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
表示测试用例不在声明顺序中运行ning
我的排序函数没有被调用,所以我怀疑 main() 正在构建一个新的测试加载器,它正在清除我的排序函数。
顾名思义,sortTestMethodsUsing
用于排序测试方法。它不用于排序 classes。 (它也不用于对不同 classes 中的方法进行排序;单独的 classes 单独处理。)
如果您在同一个 class 中有两个测试方法,将使用 sortTestMethodsUsing
来确定它们的顺序。 (此时,您会得到一个异常,因为您的函数需要 class 个名称。)
解决方案是显式创建一个 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
需要注意的是每个class中的测试方法必须命名为runTest
.
您可以手动构建一个 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
我完全意识到单元测试的顺序应该无关紧要。但是这些单元测试既用于实际单元测试也用于教学,所以我希望测试输出与测试用例源代码相匹配。
我看到有一种方法可以通过在测试加载器上设置 sortTestMethodsUsing
属性来设置排序顺序。默认是一个简单的 cmp()
调用来比较名称。因此,我尝试编写一个类似于 cmp
的函数,该函数将采用两个名称,找到它们的声明行号和它们 return 它们的 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()
当我运行这个时,我得到这个输出:
running test case A
.running test case B
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
表示测试用例不在声明顺序中运行ning
我的排序函数没有被调用,所以我怀疑 main() 正在构建一个新的测试加载器,它正在清除我的排序函数。
顾名思义,sortTestMethodsUsing
用于排序测试方法。它不用于排序 classes。 (它也不用于对不同 classes 中的方法进行排序;单独的 classes 单独处理。)
如果您在同一个 class 中有两个测试方法,将使用 sortTestMethodsUsing
来确定它们的顺序。 (此时,您会得到一个异常,因为您的函数需要 class 个名称。)
解决方案是显式创建一个 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
需要注意的是每个class中的测试方法必须命名为runTest
.
您可以手动构建一个 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