Modern QA2026Писать или генерировать: матрица принятия решений
Join

Course02 AI-Augmented Test Design

Cutting-edge · Chapter 02

Писать или генерировать: матрица принятия решений

Updated Jul 2026

Центральный вопрос

Для каждой задачи тестирования вы стоите перед выбором: писать эти тесты с нуля или сгенерировать их с помощью ИИ и курировать результат? Ни один из ответов не является всегда правильным. Этот файл предоставляет фреймворк для последовательного принятия верных решений.

Матрица принятия решений

Сценарий Писать с нуля Генерировать + курировать Почему
Новая бизнес-логика без существующих паттернов Да Нет У ИИ нет образца для ваших уникальных доменных правил
Стандартные тесты CRUD-эндпоинтов Нет Да CRUD-тесты следуют универсальным паттернам, которые ИИ хорошо знает
Сложные stateful-потоки (например, платёжные) Гибрид Гибрид Напишите скелет вручную, ИИ заполнит комбинации
Data-driven тесты (100+ комбинаций ввода) Нет Да ИИ превосходен в систематическом перечислении
Тесты, требующие глубоких доменных знаний (финансовые расчёты) Напишите логику ИИ генерирует комбинации Вы определяете правила, ИИ исследует пространство
Регрессионные тесты для известной ошибки Писать Нет Нужно точное воспроизведение конкретной ошибки
Матрица кроссбраузерного/кроссплатформенного тестирования Нет Да Комбинаторная генерация -- сильная сторона ИИ
Тесты, критичные для безопасности (аутентификация, шифрование) Гибрид Гибрид Напишите утверждения безопасности, ИИ генерирует векторы атак
Отладка нестабильных тестов Писать Нет Требует понимания причин недетерминированности
Сценарии нагрузочного тестирования Гибрид Гибрид ИИ генерирует вариации сценариев, вы настраиваете пороги

Как читать матрицу

  • Писать с нуля, когда тест требует глубокого понимания конкретной проблемы, для которой у ИИ нет контекста.
  • Генерировать + курировать, когда тест следует известному паттерну и основная ценность -- в систематическом покрытии.
  • Гибрид, когда структура теста требует человеческого суждения, но детали выигрывают от широты охвата ИИ.

Рабочий процесс курирования

Когда вы выбираете «Генерировать + курировать», следуйте этому пятишаговому процессу:

Шаг 1: ГЕНЕРАЦИЯ (5 минут)

Подайте контекст (спецификацию, существующие тесты, ограничения) и запросите конкретные цели покрытия.

claude "Read the OpenAPI spec at docs/api.yaml for the /products endpoint.
Generate 30 tests using pytest+httpx covering:
- All documented status codes
- Boundary values for price (min: 0.01, max: 99999.99)
- All enum values for category
- Auth scenarios (valid, expired, missing, wrong role)
Save to tests/test_products_generated.py"

Результат: 20-40 тестов, сгенерированных менее чем за 5 минут.

Шаг 2: СОРТИРОВКА (5 минут)

Просканируйте имена тестов. Имеют ли они смысл? Ищите галлюцинированные API или бессмысленные утверждения. Немедленно удалите явно неправильные тесты.

# Quick triage checklist:
# [ ] Test names are descriptive and follow convention
# [ ] No duplicate scenarios (different names, same test)
# [ ] All referenced fixtures exist
# [ ] All API endpoints are real (grep the route file)

# Typical triage result: delete 10-20% of generated tests

Шаг 3: ВАЛИДАЦИЯ (15 минут)

Запустите набор тестов. Исправьте ошибки импорта и отсутствующие фикстуры. Определите тесты, которые проходят, но не тестируют ничего полезного (тавтологии).

# Run and observe
pytest tests/test_products_generated.py -v --tb=short

# Common fixes needed:
# - Import paths (AI guesses, not always correctly)
# - Fixture names (AI may use generic names like "client" instead of your "api_client")
# - Base URL configuration
# - Auth helper function signatures

Шаг 4: УСИЛЕНИЕ (10 минут)

Добавьте недостающие негативные сценарии. Укрепите хрупкие утверждения. Добавьте декораторы parametrize для data-driven тестов.

# BEFORE: AI generated separate tests for each enum value
def test_category_electronics(self): ...
def test_category_clothing(self): ...
def test_category_food(self): ...

# AFTER: Consolidate into parametrized test
@pytest.mark.parametrize("category", ["electronics", "clothing", "food", "other"])
def test_valid_category_accepted(self, client, auth, category):
    response = client.post("/api/products", json={
        "name": "Test", "price": 10.00, "category": category
    }, headers=auth)
    assert response.status_code == 201
    assert response.json()["category"] == category

Шаг 5: ИНТЕГРАЦИЯ (5 минут)

Обеспечьте единообразное именование с существующим набором. Переместите в правильные директории тестов. Добавьте в конфигурацию CI.

# Rename to follow convention
mv tests/test_products_generated.py tests/test_products_api.py

# Verify it runs with the full suite
pytest tests/ -v --tb=short

# Verify coverage did not decrease
pytest tests/ --cov=app --cov-report=term-missing

Общее время: ~40 минут на 25-30 тестов продакшен-качества. Без ИИ: ~5-6 часов на то же покрытие.

Когда гибридный подход -- правильный выбор

Гибридный подход сочетает написанную человеком структуру тестов с ИИ-генерированными комбинациями. Это идеально для сложных сценариев.

Пример: тестирование платёжного потока

# HUMAN-WRITTEN: the test structure and key assertions
class TestPaymentFlow:
    """Integration tests for the full payment flow."""

    def _create_and_process_order(self, client, payment_method, amount):
        """Helper: create order, process payment, return result."""
        order = client.post("/api/orders", json={
            "items": [{"product_id": "prod-1", "quantity": 1}],
            "payment_method": payment_method,
            "amount": amount,
        })
        assert order.status_code == 201
        order_id = order.json()["id"]

        payment = client.post(f"/api/orders/{order_id}/pay")
        return payment

    def test_successful_credit_card_payment(self, client, mock_stripe):
        """Verify complete happy path for credit card payment."""
        mock_stripe.charge.return_value = {"status": "succeeded", "id": "ch_123"}

        result = self._create_and_process_order(client, "credit_card", 99.99)

        assert result.status_code == 200
        assert result.json()["payment_status"] == "completed"
        assert result.json()["stripe_charge_id"] == "ch_123"
        mock_stripe.charge.assert_called_once()

    # AI-GENERATED: permutations of failure modes
    # (generated with: "Given the test structure above, generate tests for
    #  every Stripe error: card_declined, expired_card, incorrect_cvc,
    #  processing_error, rate_limit. Follow the same pattern.")

    @pytest.mark.parametrize("stripe_error,expected_status,expected_message", [
        ("card_declined", "failed", "Your card was declined"),
        ("expired_card", "failed", "Your card has expired"),
        ("incorrect_cvc", "failed", "Incorrect CVC"),
        ("processing_error", "pending", "Payment is being processed"),
        ("rate_limit", "pending", "Please try again in a moment"),
    ])
    def test_stripe_error_handling(self, client, mock_stripe,
                                    stripe_error, expected_status, expected_message):
        mock_stripe.charge.side_effect = StripeError(stripe_error)

        result = self._create_and_process_order(client, "credit_card", 99.99)

        assert result.json()["payment_status"] == expected_status
        assert expected_message in result.json()["error_message"]

Человек пишет сложную тестовую инфраструктуру (вспомогательные методы, настройку моков, ключевые утверждения). ИИ генерирует систематические комбинации (каждый код ошибки, каждый метод оплаты, каждый диапазон сумм).

Экономика: писать vs генерировать

Фактор Писать с нуля Генерировать + курировать
Время на 50 тестов 4-6 часов 30-45 минут
Начальное качество Выше (80-90%) Ниже (65-75%)
Качество после курирования Одинаковое Одинаковое (после 30 мин ревью)
Ширина покрытия Ниже (слепые зоны человека) Выше (систематически)
Стоимость поддержки Ниже (компактный код) Чуть выше (многословность ИИ)
Единообразие Зависит от автора Высокое (один промпт = один стиль)
Мотивация Ниже (рутина для CRUD) Выше (люди фокусируются на интересных случаях)

Красные флаги: когда прекратить генерацию и начать писать

Переключайтесь с «генерировать + курировать» на «писать с нуля», когда:

  1. Вы удалили более 40% сгенерированных тестов. Промпт неправильный или домен слишком специфичен для ИИ.
  2. Вы тратите больше времени на исправление ИИ-тестов, чем потребовалось бы на их написание. Точка безубыточности обычно -- 30 минут курирования на 30 тестов.
  3. Тесты требуют понимания условий гонки или тайминга. ИИ стабильно проваливается в тестировании конкурентности.
  4. У домена нет публичной документации. ИИ не может генерировать тесты для проприетарных протоколов или внутренних API без спецификации.
  5. Вам нужно точное воспроизведение конкретной ошибки. Регрессионные тесты должны воспроизводить точные условия. ИИ генерирует общие сценарии.

Ключевой вывод

Решение «писать или генерировать» связано не с возможностями ИИ, а с тем, где ваша экспертиза приносит наибольшую ценность. Используйте ИИ для систематического, паттерн-ориентированного тестирования (CRUD, валидация, покрытие перечислений, граничные значения). Пишите вручную для доменно-специфичного тестирования, требующего суждения (бизнес-правила, безопасность, конкурентность, воспроизведение ошибок). Гибридный подход -- человеческая структура с ИИ-комбинациями -- объединяет лучшее из обоих миров.