Хуки

Что такое хуки в pytest?

подробнее

Общее понимание термина Хук

Хуки (hooks — «крючки») в программировании - это специальные точки расширения или функции, позволяющие вклиниться в выполнение чужого кода, фреймворка или системы, чтобы выполнить собственный код в определенный момент (до/после события).
Хук это не классический паттерн проектирования, а скорее технология или принцип проектирования, который делает приложения более модульными и эргономичными.

Хуки в pytest

Хуки — это специальные функции, с помощью которых можно настраивать поведение pytest без изменения самих тестов.
Хуки всегда начинаются на pytest_.
Pytest сам вызывает их в определённые моменты (старт сессии, парсинг CLI, коллекция тестов, параметризация, запуск, репортинг).

Где должны находиться функции-хуки?

подробнее

Объявление хуков производится в conftest.py.

Примеры хуков

подробнее
  • pytest_collection_modifyitems (Динамическая настройка)
    Срабатывает, когда pytest нашел все тесты, но еще не запустил их.
    Позволяет автоматически добавлять маркеры (например, slow всем тестам в папке integration) или исключать тесты на лету.
    Пример:

      def pytest_collection_modifyitems(config, items):
          for item in items:
              if "integration" in str(item.fspath):
                  item.add_marker(pytest.mark.slow)
    
  • pytest_addoption (Свои флаги в CLI)
    Позволяет добавить в команду запуска свои параметры (например, --env или --browser).
    Пример:

      def pytest_addoption(parser):
          parser.addoption("--env", action="store", default="dev", help="Choose env: dev or prod")
    
  • pytest_runtest_makereport (Слежка за результатом)
    Срабатывает после каждого этапа теста (setup, call, teardown).
    Пример:

      @pytest.hookimpl(tryfirst=True, hookwrapper=True)
      def pytest_runtest_makereport(item, call):
          outcome = yield
          rep = outcome.get_result()
          if rep.when == "call" and rep.failed:
              print(f"Тест {item.nodeid} упал!")
    

Как параметризовать тест динамически?

подробнее

С помощью хука pytest_generate_tests в conftest.py. Это позволяет генерировать параметры на лету, например, читая их из внешней базы или JSON-файла перед запуском.

Пример:

# conftest.py
def pytest_generate_tests(metafunc):
    # Если тест принимает аргумент "db_engine"
    if "db_engine" in metafunc.fixturenames:
        # Допустим, мы берем список БД из конфига или БД
        engines = ["postgres", "mysql", "sqlite"]
        # Параметризуем тест динамически
        metafunc.parametrize("db_engine", engines)

Как маркировать mark тест динамически?

подробнее

С помощью хука pytest_collection_modifyitems в conftest.py.
Этот хук вызывается, когда pytest уже нашел (собрал) все тесты, но еще не начал их выполнять. В этот момент мы можем пройтись по списку тестов (items) и добавить им маркеры.

Пример:

# conftest.py
def pytest_collection_modifyitems(config, items):
    for item in items:
        # 1. Маркировка по пути к файлу
        if "integration" in str(item.fspath):
            item.add_marker(pytest.mark.slow)
        
        # 2. Маркировка по названию функции
        if "delete" in item.name:
            item.add_marker(pytest.mark.danger)
            
        # 3. Маркировка на основе параметров (если тест параметризован)
        if hasattr(item, "callspec") and item.callspec.params.get("user") == "admin":
            item.add_marker(pytest.mark.priority)