AI-генерация unit-тестов: автоматическое покрытие кода

AI-автогенерация unit-тестов: как избавиться от тестового долга У вас есть legacy-проект на Python или TypeScript с десятками тысяч строк кода, но unit-тестов нет? Ручное написание тестов для такого объёма — недели работы, а покрытие всё равно останется неравномерным. Мы автоматизируем этот проце

Направления AI-разработки

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1440
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    997
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1264
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    712
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1002

AI-автогенерация unit-тестов: как избавиться от тестового долга

У вас есть legacy-проект на Python или TypeScript с десятками тысяч строк кода, но unit-тестов нет? Ручное написание тестов для такого объёма — недели работы, а покрытие всё равно останется неравномерным. Мы автоматизируем этот процесс с помощью AI: наш генератор анализирует код через AST, выделяет все ветвления и граничные случаи, а затем создаёт тесты, которые покрывают до 90% кода без вашего участия. Наша команда имеет более пяти лет опыта в AI/ML и реализовала свыше 30 проектов по автоматизации тестирования для различных компаний.

Как AI-генерация unit-тестов решает проблему?

Мы используем связку AST-анализа и LLM (например, GPT-4o) для глубокого понимания кода. На первом этапе парсится синтаксическое дерево: определяются функции, условия, циклы, вызовы внешних зависимостей и raise-выражения. На основе этой информации формируется контекстный промпт, который заставляет модель генерировать тесты с учётом реальной логики, а не шаблонных сценариев. Процесс полностью автоматизирован: достаточно передать путь к файлу или папке, и генератор создаст набор тестов, готовых к запуску.

import ast import inspect from langchain_openai import ChatOpenAI from pathlib import Path class UnitTestGenerator: PYTEST_PROMPT = """Сгенерируй pytest unit-тесты для функции. Код функции: ```python {function_code} 

Зависимости модуля: {imports} Анализ через AST:

  • Цикломатическая сложность: {complexity}
  • Ветки условий: {branches}
  • Вызовы внешних зависимостей: {external_calls}

Требования к тестам:

  1. Используй @pytest.mark.parametrize для наборов данных
  2. Мок внешние зависимости через pytest-mock (mocker.patch)
  3. Тестируй все ветки: каждое условие if/elif/else
  4. Тестируй raises: для каждого raise в коде
  5. Используй fixtures для переиспользуемых объектов
  6. Имена тестов: test_{function_name}_{scenario} (напр. test_calculate_tax_zero_income)

Верни только код тестов с import-секцией."""

def __init__(self):
    self.llm = ChatOpenAI(model="gpt-4o", temperature=0.1)

def generate_tests_for_file(self, source_path: str) -> str:
    source = Path(source_path).read_text(encoding="utf-8")
    tree = ast.parse(source)

    all_tests = []
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
            if node.name.startswith("_"):
                continue  # пропускаем приватные методы

            func_source = ast.get_source_segment(source, node)
            analysis = self._analyze_function(node, source)

            tests = self._generate_function_tests(func_source, analysis, source)
            all_tests.append(tests)

    return self._merge_test_files(all_tests, source_path)

def _analyze_function(self, node, source: str) -> dict:
    """AST-анализ функции перед генерацией"""
    branches = []
    external_calls = []
    raises = []

    for child in ast.walk(node):
        if isinstance(child, ast.If):
            cond = ast.get_source_segment(source, child.test)
            branches.append(cond)
        elif isinstance(child, ast.Call):
            if isinstance(child.func, ast.Attribute):
                call = f"{ast.get_source_segment(source, child.func.value)}.{child.func.attr}"
                external_calls.append(call)
        elif isinstance(child, ast.Raise):
            if child.exc:
                raises.append(ast.get_source_segment(source, child.exc))

    return {
        "complexity": self._cyclomatic_complexity(node),
        "branches": branches[:5],     # топ-5
        "external_calls": list(set(external_calls))[:5],
        "raises": raises
    }

def _generate_function_tests(self, func_code: str, analysis: dict, source: str) -> str:
    imports = self._extract_imports(source)

    result = self.llm.invoke(
        self.PYTEST_PROMPT.format(
            function_code=func_code,
            imports=imports,
            complexity=analysis["complexity"],
            branches="\n".join(analysis["branches"]),
            external_calls="\n".join(analysis["external_calls"])
        )
    )
    return result.content

## Почему важно покрывать граничные случаи?

Типичная ошибка при ручном тестировании — пропуск edge cases. Например, функция расчёта налога может корректно работать для положительных сумм, но упасть при нуле или отрицательном значении. AI-генератор через AST явно находит все `if/elif/else` и `raise`, что гарантирует покрытие каждого возможного пути. В кейсе с платежным сервисом (см. ниже) сгенерированные тесты обнаружили 11 ранее неизвестных багов, включая неверную обработку пустого списка транзакций.

## Сравнение: AI-генерация против ручного написания

| Аспект | AI-генерация | Ручное написание | Выигрыш AI |
|--------|--------------|------------------|------------|
| Время на 1000 строк кода | 10–15 минут | 8–10 часов | в 40 раз быстрее |
| Покрытие веток | 95% (AST-анализ) | 70% (среднее) | на 25% выше |
| Обнаружение багов | 1–2 бага на 100 тестов | 0.2 бага (человек пропускает) | в 5 раз больше |
| Стоимость | Низкая (только ресурсы LLM) | Высокая (часы разработчика) | экономия >80% бюджета |

## Что входит в работу?

Мы предоставляем полный набор артефактов для внедрения:

| Артефакт | Описание |
|----------|----------|
| Генератор тестов | Открытый код на Python для pytest или Jest |
| Validation loop | Автоматическое исправление ошибок, 1–2 итерации |
| CI/CD интеграция | Шаг в GitLab CI, GitHub Actions или Jenkins |
| Документация | README с примерами и API |
| Доступ к репозиторию | Git с историей коммитов |
| Обучение команды | 2 часа онлайн-сессии |
| Гарантия поддержки | 3 месяца после внедрения |

## Кейс из практики

Python-сервис обработки платежей, 12 000 строк кода, 0 unit-тестов (legacy). Запустили генератор на всю кодовую базу: 340 тестов за 45 минут. После validation loop: 298 прошли без изменений, 42 потребовали 1–2 итерации фикса. Из 298 работающих тестов — 11 упали на реальном коде, выявив баги: некорректная обработка отрицательных сумм, ошибка при пустом списке транзакций, неверный timezone в расчёте дедлайна. Наш опыт показывает, что AI-генерация не только ускоряет процесс, но и повышает качество покрытия. Свяжитесь с нами, чтобы получить демо генератора на вашем коде и оценить экономию.

## Когда AI-генерация unit-тестов оправдана?

Особенно эффективно решение для:
- Legacy-проектов без тестов (покрытие с нуля)
- Интенсивно развивающихся продуктов (каждый PR требует тестов)
- Микросервисной архитектуры (много однотипных модулей)
- Кода с высокой цикломатической сложностью (финансовые расчёты, алгоритмы)

<details>
<summary>Типичные ошибки при ручном тестировании</summary>
- Пропуск негативных сценариев (пустые входные данные, некорректные форматы)
- Отсутствие тестов на внешние зависимости (API, базы данных)
- Дублирование кода тестов (неиспользование parametrize или fixtures)
- Непокрытые ветки условий (особенно else и elif)
</details>

## Процесс внедрения

1. **Аналитика**: изучаем вашу кодовую базу, выделяем приоритетные модули.
2. **Интеграция генератора**: настраиваем скрипты под ваш стек (Python/TypeScript).
3. **Генерация тестов**: запускаем на всём проекте или выборочно.
4. **Валидация**: проверяем тесты на синтаксис и выполнение, фиксим ошибки через validation loop.
5. **Деплой**: добавляем pre-commit хук или CI-шаг для автоматической генерации при изменениях.

## Сроки

- **Один язык** (Python или TypeScript) с validation loop: 2–3 недели.
- **Мультиязычный** вариант с CI/CD интеграцией: 4–5 недель.

Стоимость рассчитывается индивидуально в зависимости от объёма кода, количества языков и требуемой кастомизации. Мы оценим ваш проект бесплатно при первом обращении. Закажите консультацию прямо сейчас.

<cite>Подробнее об AST: [Wikipedia: Abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree)</cite>