AI-система для тестирования ПО (AI QA)

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1 услугВсе 1566 услуг
AI-система для тестирования ПО (AI QA)
Сложная
от 2 недель до 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-система тестирования и QA программного обеспечения

Покрытие кода тестами на 80% звучит хорошо, пока не смотришь на то, что именно покрыто: happy path, очевидные кейсы, но не граничные условия, не интеграции между компонентами, не edge cases с неожиданными данными. AI-система QA решает не проблему «нет тестов», а проблему «тесты есть, но они не ловят то, что нужно».

Компоненты AI-системы тестирования

[Code Analysis]        [Requirement Analysis]
  AST парсинг            NLP из Jira/Confluence
       ↓                        ↓
[Test Generation Engine]
  Unit | Integration | E2E | API
       ↓
[Test Prioritization]
  Change Impact Analysis → запускать нужные тесты, не все
       ↓
[Result Analysis]
  Failure Classification + Root Cause Suggestion
       ↓
[Coverage Intelligence]
  Семантические пробелы в покрытии

AI-анализ покрытия: поиск семантических пробелов

Традиционный coverage (Istanbul, JaCoCo) считает строки. Проблема: 100% line coverage не означает, что протестированы все бизнес-сценарии.

from langchain_openai import ChatOpenAI
import ast
import textwrap

class SemanticCoverageAnalyzer:
    """Анализирует семантические пробелы в тестовом покрытии"""

    ANALYSIS_PROMPT = """Проанализируй функцию и существующие тесты.
Определи, какие бизнес-сценарии и граничные условия НЕ покрыты.

Функция:
```python
{function_code}

Существующие тесты:

{existing_tests}

Определи непокрытые сценарии:

  1. Граничные значения (empty string, None, 0, max int, negative)
  2. Комбинации параметров
  3. Сценарии ошибок (exceptions, invalid input)
  4. Конкурентные доступы (если применимо)
  5. Бизнес-правила в условиях

Для каждого: опиши сценарий + почему он важен + возможный баг если не тестировать. Верни JSON: {{gaps: [{{scenario, importance, potential_bug}}]}}"""

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

def analyze_function_coverage(
    self,
    function_source: str,
    test_source: str
) -> list[dict]:
    result = self.llm.invoke(
        self.ANALYSIS_PROMPT.format(
            function_code=function_source,
            existing_tests=test_source
        )
    )
    import json
    return json.loads(result.content)["gaps"]

def extract_functions_from_module(self, source: str) -> list[dict]:
    """Извлекает функции из Python-модуля через AST"""
    tree = ast.parse(source)
    functions = []
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
            func_source = ast.get_source_segment(source, node)
            complexity = self._calculate_cyclomatic_complexity(node)
            functions.append({
                "name": node.name,
                "source": func_source,
                "complexity": complexity,
                "line_start": node.lineno
            })
    return sorted(functions, key=lambda x: x["complexity"], reverse=True)

def _calculate_cyclomatic_complexity(self, node) -> int:
    """Цикломатическая сложность — приоритет для тестирования"""
    complexity = 1
    for child in ast.walk(node):
        if isinstance(child, (ast.If, ast.While, ast.For, ast.ExceptHandler,
                               ast.With, ast.Assert)):
            complexity += 1
        elif isinstance(child, ast.BoolOp):
            complexity += len(child.values) - 1
    return complexity

### Тест-генератор с мутационным тестированием

```python
class AITestGenerator:
    UNIT_TEST_PROMPT = """Сгенерируй pytest unit-тесты для функции.

Функция:
{function_code}

Непокрытые сценарии (сфокусируйся на них):
{gaps}

Требования:
- Используй pytest + pytest-mock
- Параметризуй через @pytest.mark.parametrize где применимо
- Для каждого теста: Arrange-Act-Assert
- Тесты на граничные значения
- Тесты на ошибочные входные данные
- Мок для внешних зависимостей

Верни только код, без объяснений."""

    async def generate_unit_tests(
        self,
        function_source: str,
        gaps: list[dict]
    ) -> str:
        gaps_text = "\n".join([
            f"- {g['scenario']}: {g['importance']}"
            for g in gaps[:5]  # топ-5 по важности
        ])

        result = await self.llm.ainvoke(
            self.UNIT_TEST_PROMPT.format(
                function_code=function_source,
                gaps=gaps_text
            )
        )
        return result.content

    async def run_mutation_testing(self, source_file: str, test_file: str) -> dict:
        """Запускает мутационное тестирование через mutmut"""
        import subprocess
        result = subprocess.run(
            ["mutmut", "run", f"--paths-to-mutate={source_file}",
             f"--tests-dir={test_file}"],
            capture_output=True, text=True
        )

        # Анализируем выживших мутантов (тесты не поймали изменение)
        survived = self._parse_survived_mutants(result.stdout)
        if survived:
            additional_tests = await self._generate_for_mutants(survived, source_file)
            return {"survived_count": len(survived), "additional_tests": additional_tests}

        return {"survived_count": 0, "mutation_score": "100%"}

Интеграция в CI/CD

# .github/workflows/ai-qa.yml
name: AI QA Analysis

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  ai-test-analysis:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # нужен для diff

      - name: Analyze changed files
        run: |
          git diff origin/main...HEAD --name-only --diff-filter=AM | \
            grep "\.py$" > changed_files.txt

      - name: Run AI coverage analysis
        run: |
          python qa_system/analyze_coverage.py \
            --changed-files changed_files.txt \
            --generate-missing-tests \
            --output coverage_report.json

      - name: Comment PR with AI findings
        uses: actions/github-script@v7
        with:
          script: |
            const report = require('./coverage_report.json')
            const comment = formatReport(report)
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              body: comment
            })

Кейс: backend сервис на Python (FastAPI), 45 000 строк кода, 380 тестов. Coverage: 74%. AI-анализ выявил 89 семантических пробелов (не строчных — сценарных), из которых 34 помечены как высокоприоритетные. Сгенерировано 67 дополнительных тестов. При прогоне: 8 из 67 тестов упали — нашли реальные баги в обработке граничных условий (None в агрегации, отрицательные количества в заказе, пустой список при сортировке).

Сроки

  • Анализ покрытия + генерация unit-тестов: 3–4 недели
  • Полная QA-система с CI/CD интеграцией: 8–10 недель
  • Мутационное тестирование и E2E: +2–3 недели