Валидационные сценарии в Vest организованы вокруг концепции suite —
изолированного блока правил, объединённых общей областью выполнения и
контекстом. Suite выступает не просто контейнером для test,
а полноценной единицей жизненного цикла, внутри которой управляется
порядок инициализации, очистки и повторного использования состояния.
Хуки уровня suite позволяют вмешиваться в процесс выполнения валидации на разных этапах: до старта набора правил, перед каждым правилом, после завершения suite и после выполнения отдельных проверок. Это формирует управляемый жизненный цикл, аналогичный тестовым фреймворкам, но адаптированный под потоковую валидацию.
Suite в Vest проходит несколько фаз:
testКаждая из этих фаз может быть расширена через соответствующие хуки. Важная особенность заключается в том, что хуки привязаны не к отдельному тесту, а к контейнеру suite, что позволяет централизованно управлять состоянием.
Хук beforeAll выполняется один раз перед стартом всех
проверок внутри suite. Он используется для подготовки глобального
состояния, загрузки данных или создания инфраструктурных
зависимостей.
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.
Он выполняется после завершения всех тестов и предназначен для
освобождения ресурсов.
suite("order_validation", () => {
afterAll(() => {
globalThis.cache = null;
});
test("order_id", "invalid order", () => {
enforce(true).isTruthy();
});
});
afterAll особенно важен при работе с внешними
источниками данных или при построении многократных прогонов
валидации.
Хук 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. Он используется для анализа результата, очистки
временных данных или сбора диагностической информации.
suite("email_validation", () => {
let lastResult;
afterEach((context) => {
lastResult = context;
});
test("format", "invalid email", () => {
enforce("test@email.com").matches(/@/);
});
});
afterEach получает доступ к контексту выполнения, что
позволяет анализировать результат конкретного шага валидации.
Vest предоставляет механизм контекста, который может использоваться внутри хуков для хранения данных, доступных во всех тестах suite.
beforeEachtestsuite("profile_validation", ({ context }) => {
beforeAll(() => {
context.roles = ["admin", "user"];
});
beforeEach(() => {
context.currentRole = "user";
});
test("role", "invalid role", () => {
enforce(context.roles).include(context.currentRole);
});
});
Контекст становится центральным механизмом передачи состояния между хуками и тестами.
Хуки уровня suite могут использоваться в асинхронном режиме, что особенно важно при работе с API или внешними источниками данных.
beforeAll может возвращать PromisebeforeEach поддерживает async операции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.
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();
});
});
});
Иерархия позволяет строить масштабируемые схемы валидации с общими зависимостями.
beforeAll(() => {
globalThis.services = createServices();
});
Используется при интеграции с API или сложными объектами бизнес-логики.
beforeEach(() => {
input = structuredClone(baseInput);
});
Обеспечивает отсутствие побочных эффектов между тестами.
afterEach((context) => {
logs.push(context);
});
Используется для аналитики поведения валидации.
afterAll(() => {
database.disconnect();
});
Критично для тестовых окружений и серверных интеграций.
Хуки уровня suite часто становятся местом концентрации побочных эффектов. Правильная организация жизненного цикла позволяет минимизировать их влияние.
Ключевой принцип — разделение:
beforeAll для долгоживущих ресурсовbeforeEach для чистого состоянияafterEach для локальной очисткиafterAll для финального освобожденияТакое разделение формирует предсказуемую модель выполнения, в которой каждый test остаётся детерминированным и независимым от предыдущих запусков.