Переиспользование тестов

Архитектурная основа повторного использования

Валидация в 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 формируется как комбинация следующих принципов:

  • выделение чистых функций проверки
  • группировка логики в доменные блоки
  • параметризация повторяющихся правил
  • использование фабрик схем
  • композиция тестов как функций
  • конфигурационное описание правил

Эти механизмы позволяют строить масштабируемые системы валидации без дублирования логики и без потери прозрачности структуры тестов.