У меня есть пакет с каталогом "тесты", в котором я храню свои модульные тесты. Мой пакет выглядит так:
Ответ 1
При выполнении немного копания, кажется, что до тех пор, пока более глубокие модули остаются импортируемыми, они будут обнаружены через python -m unittest discover
. Таким образом, решение было просто добавить файл __init__.py
в каждый каталог, чтобы сделать их пакетами.
.
├── LICENSE
├── models
│ └── __init__.py
├── README.md
├── requirements.txt
├── tc.py
├── tests
│ ├── db
│ │ ├── __init__.py # NEW
│ │ └── test_employee.py
│ ├── __init__.py # NEW
│ └── test_tc.py
└── todo.txt
Пока каждый каталог имеет __init__.py
, python -m unittest discover
может импортировать соответствующий модуль test_*
.
Ответ 2
Если вы можете добавить файл __init__.py
внутри тестов, вы можете поместить там функцию load_tests
, которая будет обрабатывать обнаружение для вас.
Если имя тестового пакета (каталог с __init__.py
) соответствует тогда пакет будет проверен на функцию load_tests. Если это существует, тогда он будет вызываться с загрузчиком, тестами, шаблоном.
Если load_tests существует, то обнаружение не возвращается в пакет, load_tests отвечает за загрузку всех тестов в пакете.
Я далек от уверенности в том, что это лучший способ, но один из способов написать эту функцию:
import os
import pkgutil
import inspect
import unittest
# Add *all* subdirectories to this module path
__path__ = [x[0] for x in os.walk(os.path.dirname(__file__))]
def load_tests(loader, suite, pattern):
for imp, modname, _ in pkgutil.walk_packages(__path__):
mod = imp.find_module(modname).load_module(modname)
for memname, memobj in inspect.getmembers(mod):
if inspect.isclass(memobj):
if issubclass(memobj, unittest.TestCase):
print("Found TestCase: {}".format(memobj))
for test in loader.loadTestsFromTestCase(memobj):
print(" Found Test: {}".format(test))
suite.addTest(test)
print("=" * 70)
return suite
Довольно уродливый, я согласен.
Сначала вы добавляете все подкаталоги в путь тестовых пакетов (Docs).
Затем вы используете pkgutil
, чтобы пройти путь, ища пакеты или модули.
Когда он находит один, он затем проверяет членов модуля, чтобы узнать, являются ли они классами, и являются ли они классами, являются ли они подклассами unittest.TestCase
. Если они есть, тесты внутри классов загружаются в набор тестов.
Итак, теперь изнутри корня проекта вы можете ввести
python -m unittest discover -p tests
Использование переключателя шаблонов -p
. Если все пойдет хорошо, вы увидите то, что я увидел, что-то вроде:
Found TestCase: <class 'test_tc.TestCase'>
Found Test: testBar (test_tc.TestCase)
Found Test: testFoo (test_tc.TestCase)
Found TestCase: <class 'test_employee.TestCase'>
Found Test: testBar (test_employee.TestCase)
Found Test: testFoo (test_employee.TestCase)
======================================================================
....
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
Что и ожидалось, каждый из моих двух файлов-примеров содержал два теста: testFoo
и testBar
каждый.
Изменить: После некоторого дополнительного копания, похоже, вы могли бы указать эту функцию как:
def load_tests(loader, suite, pattern):
for imp, modname, _ in pkgutil.walk_packages(__path__):
mod = imp.find_module(modname).load_module(modname)
for test in loader.loadTestsFromModule(mod):
print("Found Tests: {}".format(test._tests))
suite.addTests(test)
Здесь используется метод loader.loadTestsFromModule()
вместо метода loader.loadTestsFromTestCase()
, который я использовал выше. Он по-прежнему изменяет путь пакета tests
и ищет его для модулей, который, я думаю, является ключевым здесь.
Теперь результат выглядит немного по-другому, так как мы добавляем найденный testuite за один раз к нашему главному тесту testuite suite
:
python -m unittest discover -p tests
Found Tests: [<test_tc.TestCase testMethod=testBar>, <test_tc.TestCase testMethod=testFoo>]
Found Tests: [<test_employee.TestCase testMethod=testBar>, <test_employee.TestCase testMethod=testFoo>]
======================================================================
....
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK
Но мы по-прежнему получаем 4 теста, которые мы ожидали в обоих классах, в обоих подкаталогах.