Числовые правила в библиотеке Vest используются для валидации количественных ограничений: возраста, суммы заказа, длины массива, рейтингов, цен, процентов, диапазонов и любых других данных, представленных числами. Основной подход Vest заключается в декларативном описании ограничений внутри тестов, что позволяет создавать читаемые и масштабируемые сценарии проверки.
Наиболее распространённая задача — убедиться, что число не меньше определённого порога.
import vest, { test, enforce } fr om 'vest';
const suite = vest.create('profile_form', data => {
test('age', 'Возраст должен быть не менее 18 лет', () => {
enforce(data.age).greaterThanOrEquals(18);
});
});
greaterThanOrEqualsМетод:
greaterThanOrEquals(value)
проверяет:
current >= value
enforce(18).greaterThanOrEquals(18); // OK
enforce(25).greaterThanOrEquals(18); // OK
enforce(16).greaterThanOrEquals(18); // Ошибка
Для ограничения верхнего порога применяется
lessThanOrEquals.
test('discount', 'Скидка не может превышать 90%', () => {
enforce(data.discount).lessThanOrEquals(90);
});
current <= value
enforce(50).lessThanOrEquals(90); // OK
enforce(90).lessThanOrEquals(90); // OK
enforce(120).lessThanOrEquals(90); // Ошибка
Очень часто требуется ограничить число сразу с двух сторон.
test('rating', 'Рейтинг должен быть от 1 до 5', () => {
enforce(data.rating)
.greaterThanOrEquals(1)
.lessThanOrEquals(5);
});
| Значение | Результат |
|---|---|
| 0 | Ошибка |
| 1 | OK |
| 3 | OK |
| 5 | OK |
| 7 | Ошибка |
Vest поддерживает строгие сравнения.
greaterThantest('price', 'Цена должна быть больше нуля', () => {
enforce(data.price).greaterThan(0);
});
Проверка:
current > value
lessThantest('temperature', 'Температура должна быть ниже 100', () => {
enforce(data.temperature).lessThan(100);
});
Проверка:
current < value
Положительные значения обычно проверяются через
greaterThan.
test('quantity', 'Количество должно быть положительным', () => {
enforce(data.quantity).greaterThan(0);
});
1
5
100
0
-1
-50
test('temperature', 'Температура должна быть отрицательной', () => {
enforce(data.temperature).lessThan(0);
});
Для строгого сравнения используется equals.
test('step', 'Текущий шаг должен быть равен 3', () => {
enforce(data.step).equals(3);
});
Проверка использует строгое сравнение:
3 === 3
Поэтому:
enforce('3').equals(3); // Ошибка
test('status', 'Недопустимый статус', () => {
enforce(data.status).inside([1, 2, 3]);
});
1
2
3
4
10
0
Каждый тест может содержать собственный текст ошибки.
test('salary', 'Минимальная зарплата — 1000', () => {
enforce(data.salary).greaterThanOrEquals(1000);
});
При ошибке Vest вернёт:
Минимальная зарплата — 1000
test('score', 'Некорректный результат', () => {
enforce(data.score)
.isNumber()
.greaterThanOrEquals(0)
.lessThanOrEquals(100);
});
Перед числовыми сравнениями рекомендуется удостовериться, что значение действительно является числом.
test('amount', 'Сумма должна быть числом', () => {
enforce(data.amount).isNumber();
});
10
5.5
0
-100
'10'
null
undefined
NaN
test('count', 'Количество должно быть целым числом', () => {
enforce(data.count).isInteger();
});
enforce(10).isInteger(); // OK
enforce(5.5).isInteger(); // Ошибка
Vest не содержит отдельного метода для проверки float-значений, но правило легко реализуется вручную.
test('percent', 'Значение должно быть дробным', () => {
enforce(Number.isInteger(data.percent)).isFalsy();
});
JavaScript имеет ограничения точности чисел.
test('id', 'Число выходит за пределы безопасного диапазона', () => {
enforce(Number.isSafeInteger(data.id)).isTruthy();
});
-(2^53 - 1)
(2^53 - 1)
NaNПроверка NaN требует отдельного подхода.
test('value', 'Недопустимое число', () => {
enforce(Number.isNaN(data.value)).isFalsy();
});
Формы часто возвращают строки вместо чисел.
test('age', 'Возраст должен быть числом', () => {
const age = Number(data.age);
enforce(Number.isNaN(age)).isFalsy();
enforce(age).greaterThanOrEquals(18);
});
{
age: '25'
}
test('price', 'Цена должна быть в диапазоне от 10 до 1000', () => {
enforce(data.price)
.greaterThanOrEquals(10)
.lessThanOrEquals(1000);
});
test('progress', 'Процент должен быть от 0 до 100', () => {
enforce(data.progress)
.greaterThanOrEquals(0)
.lessThanOrEquals(100);
});
test('transfer', 'Сумма перевода превышает лимит', () => {
enforce(data.transfer).lessThanOrEquals(500000);
});
Хотя проверка относится к массиву, логика остаётся числовой.
test('tags', 'Можно указать максимум 5 тегов', () => {
enforce(data.tags.length).lessThanOrEquals(5);
});
test('skills', 'Необходимо указать минимум 2 навыка', () => {
enforce(data.skills.length).greaterThanOrEquals(2);
});
Vest позволяет строить сложные цепочки проверок.
test('quantity', 'Некорректное количество товара', () => {
enforce(data.quantity)
.isNumber()
.isInteger()
.greaterThan(0)
.lessThanOrEquals(1000);
});
Если встроенных методов недостаточно, используются произвольные выражения.
test('number', 'Число должно быть чётным', () => {
enforce(data.number % 2).equals(0);
});
test('number', 'Число должно быть нечётным', () => {
enforce(data.number % 2).notEquals(0);
});
test('value', 'Число должно быть кратно 5', () => {
enforce(data.value % 5).equals(0);
});
test('duration', 'Длительность должна быть от 15 до 180 минут', () => {
enforce(data.duration)
.greaterThanOrEquals(15)
.lessThanOrEquals(180);
});
optionalЧисловое поле может быть необязательным.
import vest, { test, enforce, optional } fr om 'vest';
const suite = vest.create(data => {
optional('middleScore');
test('middleScore', 'Средний балл должен быть больше 0', () => {
enforce(data.middleScore).greaterThan(0);
});
});
Если поле отсутствует — ошибка не возникнет.
test('creditLimit', 'Лимит слишком мал', () => {
if (data.accountType === 'business') {
enforce(data.creditLimit).greaterThanOrEquals(100000);
}
});
test('total', 'Итоговая сумма рассчитана неверно', () => {
const total = data.price * data.quantity;
enforce(total).lessThanOrEquals(100000);
});
function validateRange(value, min, max) {
enforce(value)
.greaterThanOrEquals(min)
.lessThanOrEquals(max);
}
Использование:
test('age', 'Возраст вне диапазона', () => {
validateRange(data.age, 18, 65);
});
data.products.forEach((product, index) => {
test(`products.${index}.quantity`, 'Количество должно быть больше 0', () => {
enforce(product.quantity).greaterThan(0);
});
});
Иногда числовые ограничения приходят с сервера.
test('amount', 'Превышен лимит операции', async () => {
const lim it = await api.getTransferLimit();
enforce(data.amount).lessThanOrEquals(lim it);
});
Ошибка:
enforce(data.age).greaterThan(18);
если:
data.age = '20'
Рекомендуемый вариант:
enforce(Number(data.age)).greaterThan(18);
NaNconst value = Number(data.amount);
enforce(value).greaterThan(0);
Если преобразование завершится ошибкой:
Number('abc') // NaN
проверка станет некорректной.
Безопасный вариант:
const value = Number(data.amount);
enforce(Number.isNaN(value)).isFalsy();
enforce(value).greaterThan(0);
isNumber со строкамиenforce('25').isNumber(); // Ошибка
Даже если строка содержит цифры, это всё ещё строка.
import vest, { test, enforce } from 'vest';
const suite = vest.create('payment_form', data => {
test('amount', 'Сумма должна быть от 100 до 100000', () => {
enforce(data.amount)
.isNumber()
.greaterThanOrEquals(100)
.lessThanOrEquals(100000);
});
test('installments', 'Количество платежей должно быть от 1 до 12', () => {
enforce(data.installments)
.isInteger()
.greaterThanOrEquals(1)
.lessThanOrEquals(12);
});
test('discount', 'Скидка должна быть от 0 до 50%', () => {
enforce(data.discount)
.greaterThanOrEquals(0)
.lessThanOrEquals(50);
});
test('products', 'Количество товаров ограничено', () => {
enforce(data.products.length)
.greaterThan(0)
.lessThanOrEquals(20);
});
});