Валидация данных в Yup строится вокруг декларативных схем, однако
стандартных правил (таких как required, min,
max, email) недостаточно для сложных
бизнес-логик. Для таких случаев используется метод
test, позволяющий внедрять произвольную
функцию проверки значения.
Метод test применяется ко всем типам схем: строкам,
числам, массивам, объектам и т.д., обеспечивая единый механизм
расширения встроенной валидации.
testБазовая форма вызова:
schema.test(name, message, testFunction)
Параметры:
Функция testFunction получает значение поля и контекст
выполнения. Она может возвращать:
true — значение валидноfalse — значение не проходит проверкуValidationError — кастомная ошибкаPromise — для асинхронной валидацииПростейшая форма:
const schema = yup.string().test(
'starts-with-a',
'Строка должна начинаться с буквы a',
(value) => {
return value?.startsWith('a');
}
);
Функция проверки вызывается с доступом к контексту this,
содержащему полезные данные:
this.value — текущее значениеthis.originalValue — исходное значение до
преобразованийthis.path — путь к полю в объектеthis.schema — текущая схемаthis.options — параметры валидацииthis.parent — родительский объект (для вложенных
схем)Пример использования контекста:
const schema = yup.object({
password: yup.string(),
confirmPassword: yup.string().test(
'match-password',
'Пароли не совпадают',
function (value) {
return value === this.parent.password;
}
)
});
createErrorДля более гибкого управления ошибками применяется
this.createError.
const schema = yup.number().test(
'positive-even',
'Число должно быть положительным и чётным',
function (value) {
if (value == null) return true;
if (value <= 0) {
return this.createError({ message: 'Число должно быть больше нуля' });
}
if (value % 2 !== 0) {
return this.createError({ message: 'Число должно быть чётным' });
}
return true;
}
);
Такой подход позволяет возвращать разные ошибки в зависимости от причины провала проверки.
Метод test поддерживает асинхронные операции, что
особенно важно при проверке уникальности значений через API или базу
данных.
const schema = yup.string().test(
'is-unique-username',
'Имя пользователя уже занято',
async function (value) {
if (!value) return true;
const response = await fetch(`/api/check-username?name=${value}`);
const result = await response.json();
return result.available;
}
);
Асинхронные тесты автоматически обрабатываются Yup при вызове
validate.
На одну схему можно навешивать несколько test, каждый из
которых выполняется последовательно.
const schema = yup.string()
.test('no-spaces', 'Нельзя использовать пробелы', value => !value.includes(' '))
.test('min-length', 'Минимум 5 символов', value => value.length >= 5);
Порядок выполнения влияет на итоговую ошибку: первая неудачная проверка прерывает цепочку.
contextYup позволяет передавать внешний контекст при валидации, который
доступен внутри test.
const schema = yup.number().test(
'max-by-role',
'Превышено допустимое значение',
function (value) {
const { role } = this.options.context;
const limits = {
admin: 1000,
user: 100
};
return value <= limits[role];
}
);
В этом случае логика проверки становится зависимой от внешних условий.
Метод test может принимать дополнительные параметры
через объектную форму:
yup.string().test({
name: 'custom-length',
message: 'Недопустимая длина',
test: function (value) {
return value.length >= 3 && value.length <= 10;
}
});
Такая форма удобна при большом количестве конфигураций и расширенной метаинформации.
undefined и nullПо умолчанию test не обязан обрабатывать
null и undefined. Их поведение определяется
другими методами схемы (required,
nullable).
Частая практика — явная проверка:
(value) => {
if (value == null) return true;
return value.startsWith('#');
}
Если схема содержит transform, значение в
test поступает уже после преобразования.
const schema = yup.number()
.transform((val, originalVal) => Number(originalVal))
.test('is-integer', 'Должно быть целым', value => Number.isInteger(value));
Для переиспользуемых правил часто выносится функция:
const isEven = (value) => value % 2 === 0;
const schema = yup.number().test(
'even-number',
'Число должно быть чётным',
isEven
);
Такой подход упрощает масштабирование валидации в больших схемах.
При наличии нескольких ошибок в рамках одного поля Yup возвращает первую найденную ошибку в порядке выполнения тестов. Это влияет на стратегию построения цепочек проверок: более критичные условия располагаются раньше.
yup.string()
.test('required-format', 'Неверный формат', value => !!value)
.test('length-check', 'Слишком короткое значение', value => value.length > 3);
При использовании в структурах данных this.path
позволяет определить точное местоположение значения:
yup.object({
users: yup.array().of(
yup.object({
age: yup.number().test(
'adult-check',
'Возраст должен быть не менее 18',
function (value) {
console.log(this.path); // users[0].age
return value >= 18;
}
)
})
)
});
undefined вместо true или
false может привести к некорректной интерпретации
результатаawait в вызывающем коде могут не
отработать корректноthis в стрелочных функциях приводит к
потере контекстаtestМетод test формирует основу расширяемости Yup,
обеспечивая возможность внедрения любой логики проверки, включая
синхронные и асинхронные сценарии, доступ к контексту формы, кастомные
ошибки и интеграцию с внешними источниками данных.