Динамическая структура тестов

Динамическая структура тестов в Vest строится вокруг идеи декларативного описания правил валидации с возможностью их генерации, модификации и ветвления во время выполнения. В отличие от статических наборов проверок, где каждая тест-функция фиксирована, динамическая модель позволяет формировать тестовые сценарии на основе входных данных, состояния формы и внешних условий.

Базовая архитектура Vest опирается на функцию test, которая регистрирует проверку внутри набора правил. Динамическая структура возникает за счёт того, что набор таких вызовов формируется не статически, а в процессе исполнения функции-валидации.

Ключевым моментом является отсутствие жёсткой фиксации количества и состава тестов до момента выполнения. Это позволяет:

  • формировать проверки на основе входных данных;
  • включать или исключать тесты по условиям;
  • создавать повторяющиеся группы проверок;
  • строить многоуровневую структуру зависимостей.
import { suite, test, enforce } from 'vest';

const validateUser = (data) => {
  return suite('user', () => {
    if (data.type === 'admin') {
      test('adminCode', 'Неверный код администратора', () => {
        enforce(data.adminCode).equals('SECRET');
      });
    }

    test('username', 'Некорректное имя пользователя', () => {
      enforce(data.username).isNotBlank();
    });
  });
};

В данном примере структура тестов изменяется в зависимости от значения data.type. Набор проверок не фиксирован, а формируется во время выполнения.

Условная генерация тестов

Одним из центральных механизмов динамической структуры является условное добавление тестов. Условия могут зависеть от:

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

Условная логика позволяет формировать дерево тестов, в котором отдельные ветви активируются только при выполнении определённых условий.

suite('payment', () => {
  if (data.paymentMethod === 'card') {
    test('cardNumber', () => {
      enforce(data.cardNumber).matches(/^\d{16}$/);
    });

    test('cvv', () => {
      enforce(data.cvv).lengthEquals(3);
    });
  }

  if (data.paymentMethod === 'crypto') {
    test('wallet', () => {
      enforce(data.wallet).isNotEmpty();
    });
  }
});

В такой структуре каждая ветка тестов представляет собой независимый поднабор, активируемый динамически.

Композиция тестовых блоков

Динамическая структура усиливается возможностью композиции. Тестовые блоки могут быть вынесены в отдельные функции и включены в основной набор условий при необходимости. Это создаёт модульную систему валидации.

const addressTests = (data) => {
  test('city', () => {
    enforce(data.city).isNotBlank();
  });

  test('zip', () => {
    enforce(data.zip).matches(/^\d{5}$/);
  });
};

suite('checkout', () => {
  test('email', () => {
    enforce(data.email).isEmail();
  });

  if (data.requiresShipping) {
    addressTests(data);
  }
});

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

Генерация тестов из массивов данных

Одним из распространённых сценариев является динамическое создание тестов на основе массивов. Это особенно важно при работе с формами, содержащими произвольное количество элементов.

suite('tags', () => {
  data.tags.forEach((tag, index) => {
    test(`tag-${index}`, () => {
      enforce(tag).isNotBlank();
    });
  });
});

Такая модель делает структуру тестов зависимой от размера входных данных, что невозможно в статической системе валидации.

Динамические зависимости между тестами

В Vest тесты могут быть связаны логически, даже если они не зависят друг от друга напрямую. Динамическая структура позволяет изменять поведение последующих тестов в зависимости от результатов предыдущих через внешние состояния или вычисления.

suite('profile', () => {
  let isAdult = false;

  test('age', () => {
    isAdult = data.age >= 18;
    enforce(data.age).greaterThan(0);
  });

  if (isAdult) {
    test('drivingLicense', () => {
      enforce(data.drivingLicense).isNotEmpty();
    });
  }
});

Хотя такая модель требует аккуратного управления состоянием, она позволяет формировать условные ветвления внутри одного набора правил.

Ленивое формирование структуры

Динамическая структура тестов в Vest обладает свойством ленивого построения: тесты регистрируются только при вызове функции валидации, а не при импорте модуля. Это даёт возможность:

  • избегать лишних вычислений;
  • строить разные наборы тестов для разных запусков;
  • адаптировать структуру под контекст исполнения.

Ленивая регистрация особенно важна при работе с формами, где структура может меняться от шага к шагу.

Уровни вложенности и иерархия

Динамическая структура может быть вложенной, формируя иерархию тестов. Вложенность выражается через композицию suite внутри suite, где каждый уровень добавляет собственный контекст.

suite('account', () => {
  test('email', () => {
    enforce(data.email).isEmail();
  });

  suite('security', () => {
    test('password', () => {
      enforce(data.password).longerThan(8);
    });

    if (data.twoFactorEnabled) {
      test('otp', () => {
        enforce(data.otp).lengthEquals(6);
      });
    }
  });
});

Такая структура формирует дерево проверок, где каждый узел может иметь собственные условия активации.

Динамическая фильтрация тестов

Внутренняя логика может включать механизмы пропуска тестов. Хотя тест зарегистрирован, он может быть фактически не выполнен в зависимости от состояния данных. Это создаёт эффект динамической фильтрации.

Фильтрация используется для оптимизации:

  • пропуска нерелевантных проверок;
  • сокращения времени выполнения;
  • разделения сценариев валидации.

Параметризованные тестовые структуры

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

const requiredField = (fieldName, value) => {
  test(fieldName, () => {
    enforce(value).isNotBlank();
  });
};

suite('form', () => {
  requiredField('firstName', data.firstName);
  requiredField('lastName', data.lastName);
  requiredField('city', data.city);
});

Параметризация позволяет строить шаблонные тестовые блоки, которые расширяются в зависимости от контекста.

Адаптивные схемы валидации

Динамическая структура тестов используется для построения адаптивных схем, где набор правил изменяется в зависимости от пользовательского сценария. Такие схемы характерны для многошаговых форм, где каждый шаг открывает новый набор условий.

Структура может меняться:

  • между шагами мастера;
  • при переключении режимов;
  • при изменении типа сущности;
  • при загрузке внешних данных.

Каждое изменение приводит к перестроению дерева тестов без изменения базовой логики.

Комбинирование синхронных и условных блоков

В динамической модели допускается смешивание фиксированных и условных блоков. Это создаёт гибридную структуру, где базовые проверки всегда присутствуют, а расширенные подключаются по условиям.

suite('order', () => {
  test('productId', () => {
    enforce(data.productId).isNotBlank();
  });

  if (data.discountApplied) {
    test('discountCode', () => {
      enforce(data.discountCode).isNotEmpty();
    });
  }

  test('quantity', () => {
    enforce(data.quantity).greaterThan(0);
  });
});

Такая организация обеспечивает баланс между стабильной основой и изменяемыми ветвями логики.

Динамическая структура как дерево исполнения

Внутренне динамические тесты формируют дерево исполнения, где каждый узел соответствует вызову test или вложенной suite. Это дерево не фиксируется заранее, а строится во время выполнения функции валидации.

Свойства такого дерева:

  • изменяемая глубина;
  • переменное количество узлов;
  • зависимость от входных данных;
  • возможность повторного построения при каждом запуске.

Именно эта модель делает структуру гибкой и адаптивной к сложным сценариям проверки данных.