← Назад к вопросам

Как PyTest понимает, какие тесты нужно запустить?

2.0 Middle🔥 242 комментариев
#Python#Фреймворки тестирования

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как 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, движок выполняет:

  1. Сбор файлов: Рекурсивно сканирует указанные директории (по умолчанию текущую), отбирая файлы по шаблонам
  2. Импорт модулей: Загружает модули Python для анализа
  3. Интроспекция: Исследует содержимое модулей, используя встроенную функцию dir() и проверку атрибутов
  4. Фильтрация: Отбирает объекты, соответствующие тестовым шаблонам

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. Критически важные детали реализации

  1. Ленивая загрузка: PyTest не выполняет код модулей при импорте для обнаружения — он анализирует только структуру
  2. Сборщик (Collector): Специальный компонент создает дерево тестовых элементов перед запуском
  3. conftest.py: Файлы с этим именем позволяют определять хуки, фикстуры и кастомизировать поведение
  4. Интеграция с setuptools: Можно запускать через python setup.py test

Элегантность подхода PyTest в том, что по умолчанию он работает "из коробки" со стандартными соглашениями, но предоставляет полный контроль через конфигурацию и хуки. Это делает фреймворк одновременно удобным для новичков и мощным для сложных проектов с тысячами тестов.

Как PyTest понимает, какие тесты нужно запустить? | PrepBro