Генерация тестовых данных с помощью AI: от схемы до готового датасета
Представьте: e-commerce база с 20 связанными таблицами, ручное создание 10 000 записей занимает неделю, а после интеграции тесты падают из-за граничного случая. Нужны реалистичные данные: не просто user_1 и [email protected]. Чтобы покрыть все сценарии, данные должны быть с правильными распределениями, граничными значениями, разными символами и связями между таблицами. Вручную это делают плохо: либо данных мало, либо они нереалистичны.
Ручная генерация тестовых данных отнимает часы и даже дни. Нужно придумать тысячи записей, согласовать внешние ключи, не забыть про null и длинные строки. AI-генерация тестовых данных решает задачу за минуты — масштабируется от сотен до миллионов записей без потери качества. Мы автоматизируем процесс с помощью LLM и кастомных промптов: создаём структурированные датасеты по схеме БД, семантический контент под ваш домен и аномальные случаи для негативных тестов. Под ключ — от схемы до готового JSON/CSV.
Наш подход даёт покрытие граничных случаев на уровне 10-15%. Вы получаете датасет, готовый для боевого тестирования. Все данные детерминированы и валидируются на лету. Свяжитесь с нами для консультации — мы подготовим демо-датасет под вашу схему.
Проблемы, решаемые AI-генерацией
Ручные датасеты содержат 10-100 записей, а нужно 100 000+. AI масштабируется без потери качества. Граничные случаи (пустые строки, SQL-инъекции, строки >1000 символов) вручную забывают в 80% проектов. При нескольких связанных таблицах ручное создание нарушает ссылочную целостность — AI-генератор строит топологический порядок и подтягивает существующие ID. Production-данные содержат персональную информацию — DataAnonymizer заменяет email, телефон, паспорт на синтетические аналоги, сохраняя структуру.
Как AI обеспечивает реалистичность данных?
Используем комбинацию: Faker для базовых провайдеров (имена, адреса); LLM (GPT-4o) для семантического контекста — диагнозы по МКБ-10, торговые наименования препаратов, суммы с логнормальным распределением; кастомные провайдеры для доменов: медицинские коды, IBAN. Температура модели 0.8 даёт разнообразие при сохранении правдоподобия.
Зачем нужны граничные случаи?
Граничные данные выявляют баги, которые не воспроизводятся на «красивых» данных. Наш генератор включает 10-15% аномальных записей в каждом батче. Это null, max int, спецсимволы, SQL-инъекции, длинные строки. В одном проекте e-commerce это помогло найти integer overflow в обработке сумм > 1 000 000 руб. — баг жил в продакшене 2 года.
| Тип граничного случая | Пример | Влияние на тестирование |
|---|---|---|
| Null значения | null, пустая строка |
Проверка обработки отсутствующих данных |
| Максимальные значения | INT_MAX, строка 1000+ символов |
Выявление переполнения, ограничений длины |
| Специальные символы | <script>, эмодзи, SQL-инъекции |
Проверка экранирования, безопасности |
| Неверные типы | строка в числовом поле | Проверка валидации типов |
| Дубликаты | Повторяющиеся уникальные ключи | Проверка уникальности, индексов |
Как работает генерация связанных данных?
Генератор строит топологический порядок зависимостей. Сначала создаются родительские таблицы, затем дочерние — с подстановкой существующих FK. Это гарантирует целостность связей. Закажите консультацию, и мы настроим генератор под вашу схему.
Процесс работы
- Аналитика — изучаем схему БД, бизнес-правила, требования к тестовым данным.
- Проектирование — настраиваем провайдеры Faker, пишем промпты для LLM, определяем распределения.
- Реализация — код генератора, интеграция с CI/CD (GitHub Actions, GitLab CI).
- Тестирование — валидация датасета: схема, дубликаты, FK, граничные случаи.
- Деплой — передача скрипта или API, обучение команды, документация.
Сравнение: ручная vs AI-генерация
| Параметр | Ручная генерация | AI-генерация (наша) |
|---|---|---|
| Скорость | 100 записей за 1 час | 100 000 записей за 10 минут |
| Покрытие граничных случаев | < 1% | 10-15% по умолчанию |
| Согласованность FK | Частые ошибки | Автоматическая |
| Анонимизация | Полуручная | Полностью автоматическая |
| Масштабируемость | Ограничена | Линейная до миллионов записей |
AI-генератор создаёт датасеты в 100 раз быстрее ручного труда.
Что входит в услугу
- Архитектура AI-генератора под вашу схему БД.
- Кастомные провайдеры Faker для домена.
- Модуль анонимизации PII.
- Интеграция с CI/CD (контейнеризация, Makefile).
- Документация по использованию и настройке.
- Обучение команды (1-2 часа онлайн).
- Поддержка 2 недели после сдачи.
Практический кейс
Наш клиент — e-commerce платформа с тестовым окружением из 50 000 строк production-данных (анонимизированных). После внедрения AI-генератора тестовый датасет расширен до 500 000 записей с реалистичными распределениями. Найдены 3 производительностных бага (slow queries), которые не воспроизводились на малом датасете. Граничные данные выявили ошибку в обработке сумм > 1 000 000 руб. (integer overflow в старом PHP-коде). Экономия времени тестирования — 4 часа в неделю.
Код генерации
from faker import Faker
from langchain_openai import ChatOpenAI
from pydantic import BaseModel
import json
import random
fake = Faker("ru_RU")
class TestDataGenerator:
SEMANTIC_PROMPT = """Сгенерируй {count} реалистичных записей для таблицы.
Схема таблицы:
{schema}
Контекст домена: {domain}
Требования:
- Данные должны выглядеть реалистично (не "test_value_1")
- Числовые значения в разумных диапазонах для домена
- Даты распределены по разным периодам
- Статусы/категории распределены неравномерно (реалистично)
- Связанные поля согласованы (напр. город и регион)
- Включи 10-15% граничных случаев: min/max значения, пустые опциональные поля
Верни JSON-массив из {count} объектов."""
def __init__(self):
self.llm = ChatOpenAI(model="gpt-4o", temperature=0.8)
def generate_for_table(
self,
schema: dict,
domain: str,
count: int = 100
) -> list[dict]:
result = self.llm.invoke(
self.SEMANTIC_PROMPT.format(
count=min(count, 50),
schema=json.dumps(schema, ensure_ascii=False, indent=2),
domain=domain
)
)
batch = json.loads(result.content)
if count > 50:
all_data = batch
while len(all_data) < count:
more = self.generate_for_table(schema, domain, min(50, count - len(all_data)))
all_data.extend(more)
return all_data[:count]
return batch
def generate_boundary_cases(self, schema: dict) -> list[dict]:
boundary_prompt = f"""Создай граничные и невалидные тестовые данные для схемы.
Схема:
{json.dumps(schema, ensure_ascii=False, indent=2)}
Создай по 2–3 случая каждого типа:
1. Пустые значения (null, "", [])
2. Максимальные значения (max int, max string length)
3. Минимальные значения (min int, 0, negative)
4. Специальные символы: <, >, &, ', ", \\n, \\t, emoji 🎉
5. SQL-injection строки (для проверки sanitization)
6. Очень длинные строки (>1000 символов)
7. Неверные типы (строка вместо числа и т.п.)
Верни JSON с пометкой expected_behavior для каждого случая."""
return json.loads(
self.llm.invoke(boundary_prompt).content
)
class RelationalDataGenerator:
def generate_dataset(
self,
schema: dict,
counts: dict
) -> dict[str, list[dict]]:
result = {}
order = self._topological_sort(schema["tables"])
for table_name in order:
table_schema = next(t for t in schema["tables"] if t["name"] == table_name)
count = counts.get(table_name, 10)
fk_pools = {}
for fk in table_schema.get("fk_relations", []):
ref_table = fk["references_table"]
if ref_table in result:
fk_pools[fk["column"]] = [r[fk["references_column"]] for r in result[ref_table]]
records = self._generate_with_fk(table_schema, count, fk_pools)
result[table_name] = records
return result
def _generate_with_fk(
self,
table_schema: dict,
count: int,
fk_pools: dict
) -> list[dict]:
records = []
for _ in range(count):
record = {}
for col in table_schema["columns"]:
if col["name"] in fk_pools:
record[col["name"]] = random.choice(fk_pools[col["name"]])
else:
record[col["name"]] = self._generate_field_value(col)
records.append(record)
return records
from faker.providers import BaseProvider
class MedicalDataProvider(BaseProvider):
DIAGNOSES = ["J00", "K21.0", "I10", "E11", "M79.3"]
MEDICATIONS = ["Амоксициллин 500мг", "Метформин 850мг", "Лизиноприл 10мг"]
def diagnosis_code(self) -> str:
return self.random_element(self.DIAGNOSES)
def medication(self) -> str:
return self.random_element(self.MEDICATIONS)
class FinanceDataProvider(BaseProvider):
def iban(self) -> str:
return f"BY{fake.numerify('##')}ALFA{fake.numerify('################')}"
def transaction_amount(self) -> float:
weights = [0.5, 0.3, 0.15, 0.05]
ranges = [(1, 100), (100, 1000), (1000, 10000), (10000, 100000)]
chosen_range = random.choices(ranges, weights=weights)[0]
return round(random.uniform(*chosen_range), 2)
class DataAnonymizer:
PII_FIELDS = {
"email": lambda v: fake.email(),
"phone": lambda v: fake.phone_number(),
"name": lambda v: fake.name(),
"inn": lambda v: fake.numerify("############"),
"passport": lambda v: f"{fake.numerify('####')} {fake.numerify('######')}",
"ip_address": lambda v: fake.ipv4_private(),
"address": lambda v: fake.address(),
}
def anonymize_dataset(self, data: list[dict], pii_field_names: list[str]) -> list[dict]:
result = []
for record in data:
anonymized = dict(record)
for field in pii_field_names:
if field in anonymized:
field_type = self._detect_field_type(field)
if field_type in self.PII_FIELDS:
anonymized[field] = self.PII_FIELDS[field_type](anonymized[field])
result.append(anonymized)
return result
Сроки ориентировочно
- Генератор структурированных данных: 1–2 недели.
- С анонимизацией и relational generation: 3–4 недели.
Стоимость рассчитывается индивидуально. Получите консультацию — мы подготовим демо-датасет под вашу схему БД. Свяжитесь с нами для оценки вашего проекта.







