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-генерація unit-тестів у 40 разів швидша за ручне написання та забезпечує на 25% вище покриття гілок.

| Аспект | 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-генерація не тільки прискорює процес, але й підвищує якість покриття. Економія для цього кейсу склала близько $30,000 на рік на команді з трьох розробників. Зв'яжіться з нами, щоб отримати демо генератора на вашому коді та оцінити економію.

## Коли 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>

<cite>Джерело: внутрішня статистика компанії</cite>