Числовые правила

Числовые правила в библиотеке 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 поддерживает строгие сравнения.

greaterThan

test('price', 'Цена должна быть больше нуля', () => {
  enforce(data.price).greaterThan(0);
});

Проверка:

current > value

lessThan

test('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);

Отсутствие проверки NaN

const 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);
  });

});