Я полностью понимаю, что порядок модульных тестов не должен иметь значения. Но эти модульные тесты так же важны для учебного использования, как и для фактического модульного тестирования, поэтому я хотел бы, чтобы тестовый результат соответствовал исходному коду тестового примера.
Я вижу, что есть способ установить порядок сортировки, установив атрибут sortTestMethodsUsing
на тестовом загрузчике. По умолчанию используется простой вызов cmp()
для лексического сравнения имен. Поэтому я попытался написать функцию cmp
-like, которая бы взяла два имени, найти номера строк их деклараций и вернуть их cmp()
-equivalent из них:
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
что тестовые примеры не выполняются в заказе декларации.
Моя функция сортировки просто не вызвана, поэтому я подозреваю, что main() создает новый тестовый загрузчик, который уничтожает мою функцию сортировки.
Решение состоит в том, чтобы создать 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
.
Как указано в названии, sortTestMethodsUsing
используется для сортировки методов тестирования. Он не используется для сортировки классов. (Он не используется для сортировки методов в разных классах, отдельные классы обрабатываются отдельно.)
Если у вас было два метода тестирования в одном классе, sortTestMethodsUsing
будет использоваться для определения их порядка. (В этот момент вы получите исключение, потому что ваша функция ожидает имена классов.)