Как PyTest понимает, какие тесты нужно запустить?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Как PyTest находит и запускает тесты
PyTest использует умную стратегию рекурсивного обнаружения тестов (test discovery), которая сочетает соглашения об именовании с гибкой конфигурацией. Вот ключевые механизмы:
1. Соглашения об именовании файлов и функций
PyTest ищет тесты в текущем каталоге и подкаталогах по следующим шаблонам:
- Файлы:
test_*.pyили*_test.py(например,test_calculator.py,calculator_test.py) - Функции:
test_*()(например,test_addition()) - Классы:
Test*(например,TestCalculator) - Методы:
test_*()или методы внутриTest*классов
Пример структуры файла:
# calculator_test.py
def test_addition():
assert 2 + 2 == 4
class TestCalculator:
def test_multiplication(self):
assert 3 * 5 == 15
def test_division(self):
assert 10 / 2 == 5
2. Рекурсивный поиск и интроспекция
Когда вы вызываете pytest, движок выполняет:
- Сбор файлов: Рекурсивно сканирует указанные директории (по умолчанию текущую), отбирая файлы по шаблонам
- Импорт модулей: Загружает модули Python для анализа
- Интроспекция: Исследует содержимое модулей, используя встроенную функцию
dir()и проверку атрибутов - Фильтрация: Отбирает объекты, соответствующие тестовым шаблонам
3. Кастомизация через конфигурацию
Вы можете изменить поведение обнаружения через:
pytest.ini: Основной конфигурационный файл
[pytest]
python_files = check_*.py
python_classes = Check*
python_functions = *_test
- Аргументы командной строки:
pytest tests/— запуск тестов из конкретной директорииpytest test_module.py— запуск тестов из одного файлаpytest test_module.py::test_func— запуск конкретной тестовой функции-k "expression"— фильтрация по имени (например,-k "addition")-m marker— запуск тестов с определенными метками
4. Механизм обнаружения на практике
Рассмотрим типичный процесс:
# test_example.py
import pytest
@pytest.mark.slow
def test_api_call():
"""Этот тест будет обнаружен"""
pass
def helper_function():
"""Эта функция НЕ будет обнаружена (не начинается с test_)"""
pass
class APITestSuite:
"""Этот класс НЕ будет обнаружен (не начинается с Test)"""
def test_method(self):
pass
PyTest пропустит helper_function() и APITestSuite, но найдет test_api_call().
5. Расширенные возможности
PyTest поддерживает дополнительные сценарии:
- Динамическое создание тестов: Через
pytest_generate_testsхуки - Параметризованные тесты: Обнаруживает каждый параметр как отдельный тест
@pytest.mark.parametrize("input,expected", [(1,2), (3,4)])
def test_increment(input, expected):
assert input + 1 == expected
- Плагины: Могут расширять или модифицировать процесс обнаружения (например,
pytest-django,pytest-asyncio) - Игнорирование тестов: Через
@pytest.mark.skipили директивуcollect_ignoreвconftest.py
6. Критически важные детали реализации
- Ленивая загрузка: PyTest не выполняет код модулей при импорте для обнаружения — он анализирует только структуру
- Сборщик (Collector): Специальный компонент создает дерево тестовых элементов перед запуском
conftest.py: Файлы с этим именем позволяют определять хуки, фикстуры и кастомизировать поведение- Интеграция с setuptools: Можно запускать через
python setup.py test
Элегантность подхода PyTest в том, что по умолчанию он работает "из коробки" со стандартными соглашениями, но предоставляет полный контроль через конфигурацию и хуки. Это делает фреймворк одновременно удобным для новичков и мощным для сложных проектов с тысячами тестов.