Писать или генерировать: матрица принятия решений
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) | Выше (люди фокусируются на интересных случаях) |
Красные флаги: когда прекратить генерацию и начать писать
Переключайтесь с «генерировать + курировать» на «писать с нуля», когда:
- Вы удалили более 40% сгенерированных тестов. Промпт неправильный или домен слишком специфичен для ИИ.
- Вы тратите больше времени на исправление ИИ-тестов, чем потребовалось бы на их написание. Точка безубыточности обычно -- 30 минут курирования на 30 тестов.
- Тесты требуют понимания условий гонки или тайминга. ИИ стабильно проваливается в тестировании конкурентности.
- У домена нет публичной документации. ИИ не может генерировать тесты для проприетарных протоколов или внутренних API без спецификации.
- Вам нужно точное воспроизведение конкретной ошибки. Регрессионные тесты должны воспроизводить точные условия. ИИ генерирует общие сценарии.
Ключевой вывод
Решение «писать или генерировать» связано не с возможностями ИИ, а с тем, где ваша экспертиза приносит наибольшую ценность. Используйте ИИ для систематического, паттерн-ориентированного тестирования (CRUD, валидация, покрытие перечислений, граничные значения). Пишите вручную для доменно-специфичного тестирования, требующего суждения (бизнес-правила, безопасность, конкурентность, воспроизведение ошибок). Гибридный подход -- человеческая структура с ИИ-комбинациями -- объединяет лучшее из обоих миров.