Схемы в Joi описывают структуру и ограничения данных, но сами по себе не решают задачу получения реалистичных входных наборов. При разработке и тестировании API, сервисов и бизнес-логики возникает необходимость в воспроизводимых и разнообразных тестовых данных, которые соответствуют правилам валидации. Схема начинает выступать не только как фильтр входных данных, но и как формальное описание их генерации.
Генерация тестовых данных на основе схемы позволяет снизить расхождение между документацией и реальным поведением системы, а также ускоряет создание моков, фикстур и нагрузочных сценариев.
Встроенный механизм default задаёт значения, которые
используются при отсутствии данных. В контексте тестирования это
становится базовым способом стабилизации входных объектов.
import Joi from 'joi';
const schema = Joi.object({
id: Joi.number().integer().default(1),
name: Joi.string().default('test user'),
active: Joi.boolean().default(true)
});
const result = schema.validate({});
При валидации пустого объекта формируется предсказуемая структура, которая может использоваться как основа для тестов.
Параметр required не генерирует данные, но фиксирует
обязательность поля, что важно при построении генераторов поверх
схемы.
Метод example() добавляет эталонные значения к схеме. Он
не участвует в валидации, но используется для документации и генерации
тестовых сценариев во внешних инструментах.
const userSchema = Joi.object({
id: Joi.number().example(1001),
email: Joi.string().email().example('user@mail.com'),
role: Joi.string().valid('admin', 'user').example('admin')
});
Такие значения часто извлекаются генераторами OpenAPI или инструментами мокирования, которые строят тестовые данные на основе метаданных схемы.
Сложные структуры данных формируются через вложенные объекты и массивы. Генерация тестовых данных в таких случаях строится от корня схемы.
const addressSchema = Joi.object({
city: Joi.string().default('Almaty'),
zip: Joi.string().default('050000')
});
const userSchema = Joi.object({
id: Joi.number().default(1),
name: Joi.string().default('Alice'),
address: addressSchema
});
Валидация пустого объекта приводит к каскадному заполнению структуры значениями по умолчанию, что позволяет использовать результат как готовую фикстуру.
alternatives() создаёт вариативные структуры, что
особенно важно для генерации разнообразных тестовых данных.
const schema = Joi.alternatives().try(
Joi.string().email(),
Joi.string().pattern(/^[0-9]+$/)
);
Для генерации тестов такой подход задаёт несколько допустимых типов данных. Внешние генераторы могут выбирать одну из ветвей, создавая наборы входных данных с разными сценариями обработки.
Валидационные ограничения (min, max,
length, pattern) одновременно служат правилами
для генерации корректных данных.
const schema = Joi.object({
username: Joi.string().min(3).max(10).alphanum(),
age: Joi.number().min(18).max(65)
});
При построении тестовых данных эти ограничения превращаются в диапазоны значений, которые могут быть использованы генератором случайных данных.
Joi не содержит встроенного полноценного генератора случайных данных, поэтому используется связка с внешними библиотеками, такими как faker-подобные генераторы.
Типовой подход заключается в разделении ответственности:
import Joi from 'joi';
import { faker } from '@faker-js/faker';
const schema = Joi.object({
id: Joi.number().integer(),
email: Joi.string().email(),
name: Joi.string()
});
function generateUser() {
return {
id: faker.number.int(),
email: faker.internet.email(),
name: faker.person.fullName()
};
}
Далее результат может проверяться через .validate(), что
обеспечивает соответствие генерации схеме.
Массивы требуют отдельного подхода, так как каждый элемент должен соответствовать под-схеме.
const schema = Joi.object({
tags: Joi.array().items(Joi.string().min(3)).min(1).max(5)
});
При генерации тестовых данных логика должна учитывать:
min/maxПример структуры:
const data = {
tags: ['node', 'api', 'test']
};
Расширение Joi позволяет внедрять пользовательскую логику, которая может участвовать в генерации тестовых данных.
const customJoi = Joi.extend((joi) => ({
type: 'evenNumber',
base: joi.number(),
validate(value, helpers) {
if (value % 2 !== 0) {
return { value, errors: helpers.error('number.even') };
}
}
}));
При генерации данных для такой схемы необходимо учитывать дополнительное условие чётности, что влияет на алгоритм генерации чисел.
Схема начинает рассматриваться как дерево описания данных. Генерация выполняется рекурсивным обходом:
itemsТиповой подход:
function generateFromSchema(schema) {
const description = schema.describe();
// обход description и генерация структуры
}
Метод describe() используется как источник
метаинформации о схеме и позволяет строить универсальные генераторы вне
зависимости от конкретной структуры данных.
Генерация тестовых данных на основе Joi не является встроенной функцией библиотеки, поэтому всегда требует внешнего слоя логики. Основные ограничения:
Несмотря на это, схемы остаются единой точкой описания данных, на основе которой возможно построение детерминированных и случайных тестовых сценариев без дублирования логики валидации.