AI-система автоматической генерации тестовых данных

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1 услугВсе 1566 услуг
AI-система автоматической генерации тестовых данных
Простая
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы
Направления AI-разработки
Этапы разработки AI-решения
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1218
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    853
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1047
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    825

AI-автогенерация тестовых данных

Реалистичные тестовые данные — не просто user_1, [email protected], password123. Для нормального покрытия нужны данные с правильными распределениями, граничными значениями, нестандартными символами и корректными связями между таблицами. Вручную это делают плохо: либо данных мало, либо они нереалистичны.

AI-генератор создаёт тестовые данные трёх типов: структурированные (по схеме БД), семантические (реалистичный контент на основе домена) и аномальные (граничные случаи для негативных тестов).

Генерация по схеме с семантическим контекстом

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),  # батчи по 50
                schema=json.dumps(schema, ensure_ascii=False, indent=2),
                domain=domain
            )
        )
        batch = json.loads(result.content)

        # Если нужно больше 50 — генерируем батчами
        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
        )

Генерация связанных данных (foreign keys)

class RelationalDataGenerator:
    """Генерирует согласованные данные для нескольких связанных таблиц"""

    def generate_dataset(
        self,
        schema: dict,   # {tables: [{name, columns, fk_relations}]}
        counts: dict    # {table_name: count}
    ) -> dict[str, list[dict]]:
        """
        Генерирует данные с учётом foreign key constraints.
        Порядок: сначала parent-таблицы, потом child.
        """
        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-полей используем существующие ID из уже сгенерированных таблиц
            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:
                    # Берём случайный существующий ID
                    record[col["name"]] = random.choice(fk_pools[col["name"]])
                else:
                    record[col["name"]] = self._generate_field_value(col)
            records.append(record)
        return records

Faker + AI для реалистичных доменных данных

# Специализированные провайдеры Faker для разных доменов
from faker.providers import BaseProvider

class MedicalDataProvider(BaseProvider):
    DIAGNOSES = ["J00", "K21.0", "I10", "E11", "M79.3"]  # МКБ-10
    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)

Anonymization: преобразование production-данных в тестовые

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

Кейс: e-commerce платформа, тестовое окружение с 50 000 строк production-данных (анонимизированных). После внедрения AI-генератора: тестовый датасет расширен до 500 000 записей с реалистичными распределениями. Найдены 3 производительностных бага (slow queries) которые не воспроизводились на малом датасете. Граничные данные выявили ошибку в обработке сумм > 1 000 000 руб. (integer overflow в старом PHP-коде).

Сроки: генератор структурированных данных: 1–2 недели; с анонимизацией и relational generation: 3–4 недели.