- Вопрос или проблема
- Структура файловой системы, используемая для воспроизведения вашего кода
- Файл run_tests.py с изменениями
- Результаты выполнения тестового набора
- Ответ или решение
- Как запустить тесты только из определённых файлов в Python с использованием unittest
- Предпосылки
- Решение проблемы
- Шаги для реализации
- Пример кода run_tests.py
- Результаты выполнения
- Заключение
Вопрос или проблема
Я хочу запустить набор модульных тестов в папке tests
. Основной код:
suite = unittest.defaultTestLoader.discover('tests')
Я хочу запустить только некоторые из этих тестов, например test_e1
, если файл e1.py
присутствует, test_e5
, если e5.py
присутствует, но не test_e2
и test_e11
(поскольку файлы e2.py
и e11.py
отсутствуют).
Я попробовал аргумент pattern
функции discoverer()
, который по умолчанию равен test_*.py
, но он не предоставляет достаточного контроля для того, что мне нужно (см. Как сопоставить конкретные файлы с шаблоном оболочки в модульном тесте обнаружения?).
Один из ответов в этой теме предлагает найти эти тесты с помощью unittest.TestLoader().loadTestsFromNames
, так что я попробовал следующий код:
file_list = []
for some_file in some_file_list:
full_filepath = os.path.join(some_dir, some_file)
if not os.path.exists(full_filepath):
continue
file_list.append("tests/test_%s.TestDocs" % some_file)
suite = unittest.TestLoader().loadTestsFromNames(file_list)
print(suite)
Имя TestDocs
– это имя класса, который наследуется от модульного теста:
class TestDocs(unittest.TestCase):
Но это показывает список проваленных тестов, таких как:
<unittest.suite.TestSuite tests=[<unittest.loader._FailedTest testMethod=tests/test_>]>
Как я могу запустить тесты только для определенного набора файлов?
Вы передаете гибридные имена файлов/объектов в loadTestsFromNames
. Уберите tests
из имени и убедитесь, что tests
присутствует в вашем модульном поисковом пути, либо путем
- Изменения
sys.path
перед вызовом метода, либо - Добавления
tests
в переменную окруженияPYTHONPATH
перед запуском ваших тестов.
Структура файловой системы, используемая для воспроизведения вашего кода
Я создал на своей системе следующую структуру:
test_project
|- tests
| |- __init__.py
| |- test_e1.py
| |- test_e2.py
|- production
| |- e1.py
|- run_tests.py
В папке tests
находятся:
- тестовые файлы
test_e1.py
,test_e2.py
- файл
__init__.py
(tests
должен быть пакетом для правильного выполнения)
Папка production
содержит файл e1.py
, но не содержит файл e2.py
, так что методы тестов из файла test_e2.py
не должны выполняться.
Файл run_tests.py
с изменениями
Я сохранил ваш код (изменив одну инструкцию и добавив инструкцию для выполнения тестов) в файл run_tests.py
:
import unittest
import os
some_dir = "/path/to/production"
some_file_list = ["e1.py", "e2.py"]
file_list = []
for some_file in some_file_list:
full_filepath = os.path.join(some_dir, some_file)
if not os.path.exists(full_filepath):
continue
# file_list.append("tests/test_%s.TestDocs" % some_file)
file_list.append("tests.test_%s.TestDocs" % some_file.split('.')[0])
suite = unittest.TestLoader().loadTestsFromNames(file_list)
#print(suite)
runner = unittest.TextTestRunner(verbosity=3)
result = runner.run(suite)
С предыдущей структурой файловой системы ваша инструкция:
file_list.append("tests/test_%s.TestDocs" % some_file)
превращается в:
file_list.append("tests.test_%s.TestDocs" % some_file.split('.')[0])
Различия:
- из имени файла
e1.py
я убрал расширение с помощью инструкцииsplit()
- я использовал точечную форму
"tests.test_%s.TestDocs" % some_file
вместо вашей"tests/test_%s.TestDocs" % some_file
(см. здесь для документации по методу loadTestsFromName() и loadTestsFromNames()).
Результаты выполнения тестового набора
Содержимое test_e1.py
:
import unittest
from production import e1
class TestDocs(unittest.TestCase):
def test_01(self):
e1.func1_of_e1()
def test_02(self):
e1.func2_of_e1()
Содержимое production/e1.py
:
def func1_of_e1():
print("func1_of_e1()")
def func2_of_e1():
print("func2_of_e1()")
Итак, вывод выполнения:
test_01 (tests.test_e1.TestDocs) ... ok
test_02 (tests.test_e1.TestDocs) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
func1_of_e1()
func2_of_e1()
Таким образом, были выполнены 2 теста (test_01
и test_02
), содержащиеся в test_e1.py
, в то время как тесты, содержащиеся в файле test_e2.py
(не показаны выше), не были выполнены, поскольку e2.py
отсутствует в папке production
.
Ответ или решение
Как запустить тесты только из определённых файлов в Python с использованием unittest
Когда вы работаете с модульными тестами в Python с помощью модуля unittest
, может возникнуть необходимость выбирать, какие тесты запускать на основе наличия определённых файлов. В этом руководстве я опишу, как вы можете создать тестовый набор, который учитывает наличие файлов, и запускать только соответствующие тесты.
Предпосылки
- Структура каталогов: Для этого примера у нас есть следующая структура каталогов:
test_project
├── tests
│ ├── __init__.py
│ ├── test_e1.py
│ ├── test_e2.py
├── production
│ ├── e1.py
├── run_tests.py
- Содержимое тестов: В
test_e1.py
мы имеем следующие тесты:
import unittest
from production import e1
class TestDocs(unittest.TestCase):
def test_01(self):
e1.func1_of_e1()
def test_02(self):
e1.func2_of_e1()
- Основные файлы: Файл
e1.py
в каталогеproduction
содержит две функции, которые мы будем тестировать.
Решение проблемы
Чтобы запустить только те тесты, которые зависят от наличия соответствующих файлов в каталоге, мы можем использовать метод loadTestsFromNames
из unittest.TestLoader
.
Шаги для реализации
- Импортируйте необходимые модули:
Вам нужно будет импортировать модули os и unittest для работы с файловой системой и тестами.
import unittest
import os
- Определите файлы и их ожидаемые тесты:
Создайте список файлов, которые нужно проверить, и соответствующих им тестов.
some_dir = "/path/to/production"
some_file_list = ["e1.py", "e2.py"]
- Создайте список тестов на основе наличия файлов:
Используйте цикл для проверки наличия файлов и генерации списка тестов.
file_list = []
for some_file in some_file_list:
full_filepath = os.path.join(some_dir, some_file)
if not os.path.exists(full_filepath):
continue
file_list.append("tests.test_%s.TestDocs" % some_file.split('.')[0])
- Загрузите и запустите тесты:
ИспользуйтеloadTestsFromNames
для создания тестового набора и выполните его.
suite = unittest.TestLoader().loadTestsFromNames(file_list)
runner = unittest.TextTestRunner(verbosity=3)
result = runner.run(suite)
Пример кода run_tests.py
Вот полный код файла run_tests.py
с учетом вышеизложенных моментов:
import unittest
import os
some_dir = "/path/to/production"
some_file_list = ["e1.py", "e2.py"]
file_list = []
for some_file in some_file_list:
full_filepath = os.path.join(some_dir, some_file)
if not os.path.exists(full_filepath):
continue
file_list.append("tests.test_%s.TestDocs" % some_file.split('.')[0])
suite = unittest.TestLoader().loadTestsFromNames(file_list)
runner = unittest.TextTestRunner(verbosity=3)
result = runner.run(suite)
Результаты выполнения
При запуске этого скрипта будут выполнены только те тесты, которые соответствуют присутствующим файлам в каталоге production
. Например, если e1.py
существует, тесты из test_e1.py
будут выполнены, в то время как тесты из test_e2.py
будут проигнорированы.
Вывод консольного приложения будет показывать результаты тестов, включая успешные и неуспешные тесты, что позволит вам эффективно управлять процессом тестирования и поддерживать высокое качество кода.
Заключение
Следуя приведённым выше шагам, вы сможете гибко управлять тестированием вашего Python проекта, запускать тесты только из тех файлов, которые актуальны, обеспечивая тем самым оптимизацию и своевременную проверку функциональности вашего кода.