Хуки уровня suite

Валидационные сценарии в Vest организованы вокруг концепции suite — изолированного блока правил, объединённых общей областью выполнения и контекстом. Suite выступает не просто контейнером для test, а полноценной единицей жизненного цикла, внутри которой управляется порядок инициализации, очистки и повторного использования состояния.

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


Жизненный цикл suite и точки расширения

Suite в Vest проходит несколько фаз:

  • инициализация контекста
  • подготовка зависимостей
  • выполнение набора test
  • агрегация результата
  • завершение и очистка

Каждая из этих фаз может быть расширена через соответствующие хуки. Важная особенность заключается в том, что хуки привязаны не к отдельному тесту, а к контейнеру suite, что позволяет централизованно управлять состоянием.


beforeAll: инициализация до выполнения suite

Хук beforeAll выполняется один раз перед стартом всех проверок внутри suite. Он используется для подготовки глобального состояния, загрузки данных или создания инфраструктурных зависимостей.

Основные сценарии применения

  • загрузка справочников
  • подготовка моков API
  • инициализация общего контекста валидации
  • кэширование вычисляемых данных

Пример использования

import { suite, test, enforce } from "vest";

suite("user_form", () => {

  beforeAll(() => {
    globalThis.countries = ["KZ", "RU", "US"];
  });

  test("country", "invalid country", () => {
    enforce(globalThis.countries).include("KZ");
  });

});

Характерная особенность beforeAll — выполнение строго один раз, независимо от количества test внутри suite.


afterAll: завершение и очистка suite

Хук afterAll используется для финализации работы suite. Он выполняется после завершения всех тестов и предназначен для освобождения ресурсов.

Типичные задачи

  • очистка временных данных
  • сброс глобального состояния
  • закрытие соединений
  • логирование итогов выполнения suite

Пример

suite("order_validation", () => {

  afterAll(() => {
    globalThis.cache = null;
  });

  test("order_id", "invalid order", () => {
    enforce(true).isTruthy();
  });

});

afterAll особенно важен при работе с внешними источниками данных или при построении многократных прогонов валидации.


beforeEach: подготовка перед каждым test

Хук beforeEach вызывается перед каждым test внутри suite. Его основная роль — создание изолированного состояния для каждой проверки.

Ключевые задачи

  • сброс контекста
  • создание чистых входных данных
  • подготовка вычисляемых значений
  • перезапись временных переменных

Пример

suite("password_validation", () => {

  let value;

  beforeEach(() => {
    value = "default_password";
  });

  test("length", "too short password", () => {
    enforce(value.length).greaterThan(8);
  });

  test("contains_number", "missing number", () => {
    enforce(/\d/.test(value)).isTruthy();
  });

});

Важная характеристика beforeEach — гарантированная изоляция тестов, предотвращающая утечку состояния между проверками.


afterEach: постобработка после test

Хук afterEach выполняется сразу после каждого test. Он используется для анализа результата, очистки временных данных или сбора диагностической информации.

Применение

  • логирование результата конкретного теста
  • очистка промежуточных значений
  • сбор метрик выполнения
  • отладочные операции

Пример

suite("email_validation", () => {

  let lastResult;

  afterEach((context) => {
    lastResult = context;
  });

  test("format", "invalid email", () => {
    enforce("test@email.com").matches(/@/);
  });

});

afterEach получает доступ к контексту выполнения, что позволяет анализировать результат конкретного шага валидации.


Контекст suite и передача состояния

Vest предоставляет механизм контекста, который может использоваться внутри хуков для хранения данных, доступных во всех тестах suite.

Особенности контекста

  • существует в рамках одного suite
  • доступен во всех хуках
  • может быть изменён в beforeEach
  • может использоваться в test

Пример работы с контекстом

suite("profile_validation", ({ context }) => {

  beforeAll(() => {
    context.roles = ["admin", "user"];
  });

  beforeEach(() => {
    context.currentRole = "user";
  });

  test("role", "invalid role", () => {
    enforce(context.roles).include(context.currentRole);
  });

});

Контекст становится центральным механизмом передачи состояния между хуками и тестами.


Асинхронные хуки suite

Хуки уровня suite могут использоваться в асинхронном режиме, что особенно важно при работе с API или внешними источниками данных.

Особенности асинхронности

  • beforeAll может возвращать Promise
  • beforeEach поддерживает async операции
  • выполнение suite блокируется до завершения хуков
  • ошибки в хуках прерывают выполнение suite

Пример

suite("remote_validation", () => {

  beforeAll(async () => {
    globalThis.config = await fetch("/config").then(r => r.json());
  });

  test("config_loaded", "missing config", () => {
    enforce(globalThis.config).isNotNull();
  });

});

Асинхронные хуки позволяют интегрировать Vest в системы, где данные формируются динамически.


Вложенные suite и наследование хуков

При использовании вложенных suite структура жизненного цикла становится иерархической. Хуки верхнего уровня выполняются раньше, чем хуки дочерних suite.

Поведение

  • beforeAll родителя выполняется до всех детей
  • afterAll родителя выполняется после всех детей
  • beforeEach применяется каскадно
  • afterEach выполняется в обратном порядке

Пример

suite("auth", () => {

  beforeAll(() => {
    globalThis.token = "abc";
  });

  suite("login", () => {

    beforeEach(() => {
      globalThis.user = { name: "test" };
    });

    test("valid_user", "invalid", () => {
      enforce(globalThis.user.name).isNotEmpty();
    });

  });

});

Иерархия позволяет строить масштабируемые схемы валидации с общими зависимостями.


Паттерны использования suite-хуков

Централизованная инициализация зависимостей

beforeAll(() => {
  globalThis.services = createServices();
});

Используется при интеграции с API или сложными объектами бизнес-логики.


Изоляция данных через beforeEach

beforeEach(() => {
  input = structuredClone(baseInput);
});

Обеспечивает отсутствие побочных эффектов между тестами.


Сбор диагностической информации через afterEach

afterEach((context) => {
  logs.push(context);
});

Используется для аналитики поведения валидации.


Очистка ресурсов через afterAll

afterAll(() => {
  database.disconnect();
});

Критично для тестовых окружений и серверных интеграций.


Управление побочными эффектами в suite

Хуки уровня suite часто становятся местом концентрации побочных эффектов. Правильная организация жизненного цикла позволяет минимизировать их влияние.

Ключевой принцип — разделение:

  • beforeAll для долгоживущих ресурсов
  • beforeEach для чистого состояния
  • afterEach для локальной очистки
  • afterAll для финального освобождения

Такое разделение формирует предсказуемую модель выполнения, в которой каждый test остаётся детерминированным и независимым от предыдущих запусков.