AI-генерація тестових даних: схеми, граничні випадки, анонімізація

Ручне створення тестових даних забирає години і часто не покриває граничні випадки, через що тести пропускають помилки. Ми автоматизуємо генерацію за допомогою AI: створюємо реалістичні датасети за вашою схемою, включаючи аномалії та анонімізацію. Наша команда реалізує проєкт під ключ — від аудиту до готового набору даних з подальшою підтримкою.
Часті запитання

Генерація тестових даних за допомогою 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 при обробці великих сум — баг жив у продакшені 2 роки.

Тип граничного випадку Приклад Вплив на тестування
Null значення null, порожній рядок Перевірка обробки відсутніх даних
Максимальні значення INT_MAX, рядок 1000+ символів Виявлення переповнення, обмежень довжини
Спеціальні символи <script>, емодзі, SQL-ін'єкції Перевірка екранування, безпеки
Неправильні типи рядок у числовому полі Перевірка валідації типів
Дублікати Повторювані унікальні ключі Перевірка унікальності, індексів

Як працює генерація зв'язаних даних?

Генератор будує топологічний порядок залежностей. Спочатку створюються батьківські таблиці, потім дочірні — з підстановкою існуючих FK. Це гарантує цілісність зв'язків. Замовте консультацію, і ми налаштуємо генератор під вашу схему.

Процес роботи

  1. Аналітика — вивчаємо схему БД, бізнес-правила, вимоги до тестових даних.
  2. Проектування — налаштовуємо провайдери Faker, пишемо промпти для LLM, визначаємо розподіли.
  3. Реалізація — код генератора, інтеграція з CI/CD (GitHub Actions, GitLab CI).
  4. Тестування — валідація датасету: схема, дублікати, FK, граничні випадки.
  5. Деплой — передача скрипту або 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), які не відтворювалися на малому датасеті. Граничні дані виявили помилку в обробці великих сум (integer overflow у старому PHP-коді). Економія часу тестування — 4 години на тиждень.

Код генерації

from faker import Faker
from langchain_openai import ChatOpenAI
from pydantic import BaseModel
import json
import random

fake = Faker("uk_UA")

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 тижні.

Вартість розраховується індивідуально. Отримайте консультацію — ми підготуємо демо-датасет під вашу схему БД. Зв'яжіться з нами для оцінки вашого проєкту.

Напрямки AI-розробки

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка логотипу компанії B2B Advance
    Розробка логотипу компанії B2B Advance
    815
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1140