Skip to content

ts-stub-genСтабы данных для тестов из типов TypeScript

Хелперы собирают корректный объект целиком — в тесте вы указываете только важные для него поля

Зачем это нужно

Тестируемый код обращается за данными наружу — например, веб-приложение запрашивает сервер. В тестах эти ответы подменяют заглушками, и заглушки быстро разрастаются: чтобы объект был корректным, приходится заполнять десятки полей, из которых тесту важны два-три. Код теста тонет в неважных деталях, а при изменении типов заглушки рассыпаются по всему проекту.

ts-stub-gen решает это генерацией: для каждого типа создаётся функция, возвращающая объект со значениями по умолчанию. Параметр overrides принимает только те поля, которые нужны тесту:

ts
// вручную: что здесь проверяется?
const response: Response = {
  requestId: "r-1",
  status: "ok",
  pagination: { page: 1, perPage: 20, total: 1 },
  account: { id: "42", balance: 0, tags: [], createdAt: new Date(0) },
};

// с ts-stub-gen: тест проверяет счёт с id "42" — это и видно
const response = GetStubResponse({
  account: GetStubAccount({ id: "42" }),
});

Хелперы компонуются: для вложенного поля передаётся результат хелпера вложенного типа. Так собираются сложные иерархии данных без единого лишнего поля в коде теста.

Что поддерживается

ts-stub-gen работает с типами, которые описывают данные без поведения: интерфейсами, алиасами типов и enum. Хелперы генерируются для всех экспортированных типов из entry; типы, на которые они ссылаются, подхватываются автоматически — в том числе из других файлов, через реэкспорты и переименованные импорты. Неэкспортированные типы раскрываются прямо внутри стаба.

Всё, что умеет вычислять компилятор TypeScript, раскрывается на этапе генерации:

  • инстанцирования дженериков — Paginated<Account> превращается в конкретную структуру;
  • встроенные утилиты — Partial, Pick, Omit, Required, Readonly, ReturnType, NonNullable и другие;
  • пересечения A & B, mapped types и indexed access (Settings['a']);
  • типы, выведенные из библиотек описания схем: z.infer (zod), v.InferOutput (valibot), t.TypeOf (io-ts) и аналогичные.

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

Ограничения

Конструкции, которые нельзя превратить в данные, получают значение undefined as any и сопровождаются предупреждением — уровни настраиваются в секции warnings:

  • функции и методы в типах — предупреждение behavior-in-type;
  • классы и контейнеры Map, Set, Promise — предупреждение unsupported-type;
  • тип, объявленный через export default, пропускается — предупреждение default-export;
  • квалифицированный доступ к собственным типам через TS namespace (A.B.C) пока не поддерживается.

Enum, участвующий в типах данных, должен быть экспортирован: иначе сгенерированному коду неоткуда его импортировать, и генерация завершается ошибкой enum-not-exported.

Отдельное ограничение связано с рекурсией: тип с обязательным полем, ссылающимся на самого себя (next: LinkedList), невозможно представить конечным объектом, поэтому вызов такого хелпера приведёт к бесконечной рекурсии в рантайме. Рекурсия через массив (children: Tree[]) или опциональное поле работает нормально.

Поля типа Date получают значение new Date(0). Если ваша команда не допускает Date в данных — например, ради JSON-сериализуемости, — включите предупреждение date-type в уровень warn или error.