Динамические правила в Vest представляют собой механизм условного формирования и выполнения валидационных тестов в зависимости от текущего состояния данных, внешних параметров или значений других полей. Такой подход позволяет строить адаптивные схемы проверки, которые изменяются во время выполнения, а не фиксируются заранее.
В основе работы Vest лежит декларативная модель, в которой набор
проверок описывается через suite. Каждое правило
оформляется как тест, выполняемый в рамках текущего контекста
данных.
Контекст включает:
Динамическое поведение достигается за счёт того, что выполнение тестов может быть условно включено или исключено на основании этого контекста.
Основной механизм динамических правил в Vest основан на условном выполнении тестов. На практике это реализуется через функции условного контроля выполнения, позволяющие пропускать или включать проверки.
Типовой подход:
import { test, skip } from "vest";
test("username", "username is required", () => {
if (!get().username) {
skip();
}
expect(get().username).toBeTruthy();
});
В этом примере логика проверки зависит от наличия значения. Если поле отсутствует, тест не выполняется как строгая проверка, а его поведение изменяется.
Такой механизм позволяет:
Vest поддерживает внешнее состояние, которое может влиять на правила валидации. Это состояние передаётся в suite и доступно внутри тестов.
import { create, test } from "vest";
const suite = create((data, config) => {
test("password", "too short", () => {
if (config.strictMode) {
expect(data.password).toHaveLengthGreaterThan(10);
} else {
expect(data.password).toHaveLengthGreaterThan(6);
}
});
});
Здесь поведение проверки зависит от параметра
strictMode. Таким образом, одна и та же схема тестов может
адаптироваться к различным сценариям использования.
Динамическое состояние используется для:
Одной из ключевых областей применения динамических правил является обработка зависимых полей. Валидация одного поля может зависеть от значения другого.
test("confirmPassword", "passwords do not match", () => {
if (get().password) {
expect(get().confirmPassword).toBe(get().password);
} else {
skip();
}
});
Здесь правило становится активным только при наличии базового значения. Такая модель позволяет:
Vest позволяет реализовывать выборочное выполнение тестов через условные конструкции внутри suite. Это особенно важно при сложных формах, где часть полей появляется только при определённых условиях.
const suite = create((data) => {
if (data.type === "business") {
test("companyName", "required", () => {
expect(data.companyName).toBeTruthy();
});
}
if (data.type === "individual") {
test("firstName", "required", () => {
expect(data.firstName).toBeTruthy();
});
}
});
Такой подход формирует динамическую структуру валидации, которая изменяется в зависимости от входных данных.
Ключевые особенности:
Динамические правила часто строятся на основе вычисляемых условий, которые могут включать сложные выражения.
const suite = create((data) => {
const isAdult = data.age >= 18;
const hasConsent = data.consent === true;
test("access", "not allowed", () => {
if (!isAdult && !hasConsent) {
expect(false).toBe(true);
}
});
});
Такая модель позволяет переносить бизнес-логику в слой валидации, сохраняя при этом читаемость структуры.
В реальных сценариях динамические правила редко используются изолированно. Чаще применяется комбинация условий, состояния и зависимостей.
const suite = create((data, config) => {
const requiresAddress = config.mode === "shipping";
if (requiresAddress) {
test("address", "required", () => {
expect(data.address).toBeTruthy();
});
test("postalCode", "invalid", () => {
expect(data.postalCode).toMatch(/^\d{6}$/);
});
}
if (data.country === "local") {
test("region", "required", () => {
expect(data.region).toBeTruthy();
});
}
});
Здесь одновременно используются:
Используется для форм, где разные блоки данных активируются по типу сущности.
if (data.mode === "A") {
// набор тестов A
}
if (data.mode === "B") {
// набор тестов B
}
Один тест активируется только при прохождении другого условия.
test("discount", "invalid", () => {
if (data.coupon) {
expect(data.discount).toBeGreaterThan(0);
}
});
Логика валидации изменяется через внешний конфиг.
const strict = config.strict;
test("field", "invalid", () => {
if (strict) {
expect(data.field).toMatchStrictRule();
} else {
expect(data.field).toMatchLooseRule();
}
});
При использовании динамических правил важно учитывать стоимость пересоздания suite и количество условных ветвлений.
Основные принципы:
Неэффективный подход:
test("field", () => {
if (a) {
if (b) {
if (c) {
// проверка
}
}
}
});
Более оптимальный подход:
if (a && b && c) {
test("field", () => {
// проверка
});
}
В сложных формах динамические правила могут строиться как комбинация нескольких уровней условий: глобального режима, состояния формы и локальных зависимостей.
const suite = create((data, config) => {
const isEditable = config.mode !== "readonly";
if (!isEditable) return;
if (data.step >= 2) {
test("email", "invalid", () => {
expect(data.email).toContain("@");
});
}
if (data.step >= 3 && data.subscribed) {
test("preferences", "required", () => {
expect(data.preferences).toBeDefined();
});
}
});
Такая структура позволяет моделировать многоступенчатые формы, где каждая стадия активирует свой набор проверок, а правила изменяются по мере заполнения данных.