Валидация в Vest строится вокруг декларативного описания правил, где каждый тест является изолированной функцией с доступом к общему состоянию набора через контекст выполнения. Эта модель позволяет выносить повторяемые проверки в отдельные абстракции без потери читаемости и предсказуемости.
Повторное использование тестов реализуется на нескольких уровнях:
Каждый уровень решает свою задачу и не заменяет остальные, а дополняет их.
Наиболее частый сценарий — повторное использование отдельных правил валидации. Вместо дублирования логики создаются функции-обёртки.
import { test, enforce } from 'vest';
const isRequired = (fieldName, value) => {
test(`${fieldName}_required`, 'Поле обязательно', () => {
enforce(value).isNotEmpty();
});
};
Такая функция может быть использована в любом наборе тестов:
const suite = createVest('form', (data) => {
isRequired('username', data.username);
isRequired('email', data.email);
});
Переиспользование на уровне функций уменьшает связность и позволяет централизованно изменять бизнес-логику.
Vest предоставляет механизм группировки через group, что
позволяет выделять логические блоки и повторно использовать целые наборы
проверок.
import { group, test, enforce } from 'vest';
const credentialsGroup = (data) => {
group('credentials', () => {
test('username_format', 'Некорректный логин', () => {
enforce(data.username).matches(/^[a-zA-Z0-9_]+$/);
});
test('password_strength', 'Слабый пароль', () => {
enforce(data.password).longerThan(8);
});
});
};
Далее группа подключается как единый модуль:
const suite = createVest('signup', (data) => {
credentialsGroup(data);
});
Такой подход обеспечивает модульность уровня доменной логики.
Когда структура форм или сущностей повторяется, используется фабрика схем. Она возвращает функцию-валидатор с заранее собранным набором правил.
const createEmailFieldTests = (field) => (data) => {
test(`${field}_email_format`, 'Неверный email', () => {
enforce(data[field]).matches(/.+@.+\..+/);
});
test(`${field}_email_required`, 'Email обязателен', () => {
enforce(data[field]).isNotEmpty();
});
};
Применение:
const emailTests = createEmailFieldTests('email');
const suite = createVest('profile', (data) => {
emailTests(data);
});
Фабрики позволяют стандартизировать проверки для повторяющихся компонентов интерфейса.
Одним из ключевых способов устранения дублирования является параметризация. Вместо создания отдельных тестов для каждого поля используется универсальная функция.
const minLengthTest = (field, min) => {
test(`${field}_min_length`, `Минимальная длина ${min}`, (data) => {
enforce(data[field]).longerThan(min);
});
};
Использование:
const suite = createVest('form', (data) => {
minLengthTest('username', 3);
minLengthTest('password', 8);
});
Параметризация снижает количество кода и делает правила декларативными.
В сложных приложениях часто выделяется базовая схема, которая расширяется другими наборами тестов.
const baseUserSuite = (data) => {
test('id_present', 'ID обязателен', () => {
enforce(data.id).isNotEmpty();
});
test('created_at_valid', 'Некорректная дата', () => {
enforce(data.createdAt).isDate();
});
};
Расширение:
const extendedUserSuite = (data) => {
baseUserSuite(data);
test('role_valid', 'Некорректная роль', () => {
enforce(data.role).isIn(['admin', 'user']);
});
};
Такой подход формирует иерархию правил без наследования в классическом ООП смысле.
В Vest тесты являются функциями, поэтому их можно комбинировать как обычные вычисления.
const hasValue = (value) => enforce(value).isNotEmpty();
const isLongEnough = (value, len) => enforce(value).longerThan(len);
Композиция внутри теста:
test('password_rule', 'Пароль не соответствует требованиям', () => {
hasValue(data.password);
isLongEnough(data.password, 8);
});
Композиционный стиль делает правила более читаемыми и переносимыми.
Повторное использование повышается, когда валидация не содержит доменных вычислений напрямую. Вместо этого используются чистые функции.
const isCorporateEmail = (email) => email.endsWith('@company.com');
const corporateEmailTest = (data) => {
test('email_corporate', 'Только корпоративные адреса', () => {
enforce(isCorporateEmail(data.email)).isTruthy();
});
};
Такие функции легко использовать в разных наборах без привязки к форме.
При росте приложения тестовые наборы собираются как конструктор из независимых блоков.
const addressTests = (data) => {
test('address_required', 'Адрес обязателен', () => {
enforce(data.address).isNotEmpty();
});
};
const paymentTests = (data) => {
test('payment_method', 'Некорректный метод оплаты', () => {
enforce(data.paymentMethod).isIn(['card', 'paypal']);
});
};
const checkoutSuite = (data) => {
addressTests(data);
paymentTests(data);
};
Модульность позволяет изменять отдельные части без влияния на общую структуру.
Когда набор правил зависит от конфигурации, используется декларативное описание.
const rules = [
{ field: 'username', min: 3 },
{ field: 'password', min: 8 },
];
const applyRules = (data) => {
rules.forEach(({ field, min }) => {
test(`${field}_min`, `Минимум ${min} символов`, () => {
enforce(data[field]).longerThan(min);
});
});
};
Такой подход позволяет генерировать тесты динамически без дублирования логики.
Одна и та же логика часто применяется к разным моделям данных. Вместо копирования создаются общие валидаторы.
const textFieldValidator = (field) => (data) => {
test(`${field}_not_empty`, 'Поле пустое', () => {
enforce(data[field]).isNotEmpty();
});
};
Использование в разных контекстах:
const userSuite = (data) => {
textFieldValidator('username')(data);
};
const postSuite = (data) => {
textFieldValidator('title')(data);
};
Переиспользование тестов в Vest формируется как комбинация следующих принципов:
Эти механизмы позволяют строить масштабируемые системы валидации без дублирования логики и без потери прозрачности структуры тестов.