Контрольный список для тестирования проекта

Основной принцип - не тестируем то что ужеткстировали и не тестируем очивидную логику

1. Не тестируем то, что можно протестировать BDD-тестами

BDD покрывает:

  • ✅ Пользовательские сценарии (создал платеж → получил результат)
  • ✅ HTTP-эндпоинты (GET/POST запросы)
  • ✅ Интеграцию между компонентами
  • ✅ Бизнес-требования в терминах пользователя

Unit-тесты покрывают:

  • ✅ Внутреннюю логику, которую не видно извне
  • ✅ Асинхронные задачи (tasks, workers)
  • ✅ Сигналы и хуки
  • ✅ Утилиты, экспортеры, сервисы

2. Не тестируем очевидную и простую логику

Не тестируем:

  • ❌ Стандартное поведение Django/фреймворка
    • auto_now_add для дат
    • default= для полей модели
    • on_delete=CASCADE
    • Индексы БД (это гарантирует Django)
  • ❌ Геттеры/сеттеры без логики
  • ❌ Простые присваивания атрибутов
  • ❌ Строковое представление без сложной логики

Тестируем:

  • ✅ Кастомную логику в save(), clean()
  • ✅ Валидацию с бизнес-правилами
  • ✅ Вычисляемые свойства с условиями

3. Тестируем только критически важную или сложную логику

Критичные блоки (приоритет 🔴):

  • 🔴 Финансовые операции (суммы, комиссии, конвертация)
  • 🔴 Транзакции и блокировки (select_for_update)
  • 🔴 Асинхронные задачи (потеря данных = инцидент)
  • 🔴 Сигналы с побочными эффектами
  • 🔴 Интеграции с внешними системами (API, blockchain)
  • 🔴 Безопасность (проверка прав, валидация токенов)

Сложные блоки (приоритет 🟡):

  • 🟡 Агрегация данных с оптимизацией
  • 🟡 Кэширование с инвалидацией
  • 🟡 Сложные условия и ветвления
  • 🟡 Регулярные выражения и парсинг

Не критичные (приоритет 🟢):

  • 🟢 CRUD без бизнес-логики
  • 🟢 Простые фильтры и сортировки
  • 🟢 Отображение данных (форматирование)

📋 Чеклист перед написанием теста

Вопросы для самопроверки

  • Этот тест можно заменить BDD-сценарием?
    • Если да → пишем BDD, не unit
    • Если нет → продолжаем
  • Это стандартное поведение фреймворка?
    • Если да → не тестируем
    • Если нет → продолжаем
  • Ошибка в этом коде стоит денег/данных?
    • Если да → тестируем обязательно
    • Если нет → оцениваем сложность
  • Здесь есть сложная логика (условия, циклы, расчёты)?
    • Если да → тестируем
    • Если нет → возможно, не нужно
  • Этот код уже покрыт другими тестами?
    • Если да → не дублируем
    • Если нет → продолжаем

🧪 Структура теста

Хороший тест

def test_confirm_payment_success(self):
    """Тест: успешное подтверждение платежа."""
    # Arrange: подготовка данных
    payment = PaymentInfo.objects.create(
        account=self.account,
        confirmed=False,
        status="pending",
    )

    # Act: вызов тестируемой логики
    result = process_blockchain_event(payment.id.int)

    # Assert: проверка результата
    payment.refresh_from_db()
    assert result == f"Payment {payment.id} confirmed."
    assert payment.confirmed is True
    assert payment.status == "success"

Плохой тест (избыточный)

def test_id_is_uuid(self):
    """Тест: id имеет тип UUID."""
    # ❌ Django гарантирует это автоматически
    payment = PaymentInfo.objects.create(account=self.account)
    assert isinstance(payment.id, uuid.UUID)

def test_default_confirmed_is_false(self):
    """Тест: confirmed по умолчанию False."""
    # ❌ Проверяет default= в модели — это гарантирует Django
    payment = PaymentInfo.objects.create(account=self.account)
    assert payment.confirmed is False

📁 Организация тестов

Структура папок

payment/
├── tests/
│   ├── __init__.py          # Просто docstring, без __all__
│   ├── test_tasks.py        # Критично: асинхронные задачи
│   ├── test_signals.py      # Критично: сигналы
│   ├── test_exporters.py    # Важно: экспорт метрик
│   ├── test_middleware.py   # Средне: middleware
│   └── test_models.py       # Дополнение: бизнес-логика моделей
├── tasks.py
├── signals.py
└── models.py

Именование

  • test_<функция>_<сценарий>.pytest_tasks_process_blockchain_event.py
  • ✅ Класс: <Функция>TestProcessBlockchainEventTest
  • ✅ Метод: test_<сценарий>_<ожидаемое>test_confirm_payment_success

🔍 Что покрывать тестами (примеры)

✅ Покрыть тестами

Блок Почему Приоритет
tasks.py Асинхронная задача, потеря = инцидент 🔴
signals.py Скрытая логика, не видна в BDD 🔴
exporters.py Агрегация с оптимизацией (один запрос) 🟡
middleware.py Regex,边界 cases 🟢
models.save() Кастомная логика в save() 🟡
validators.py Бизнес-валидация 🟡

❌ Не покрывать тестами

Блок Почему
models.id = UUIDField(...) Гарантирует Django
models.created_at = auto_now_add Гарантирует Django
models ForeignKey(on_delete=CASCADE) Гарантирует Django
models.status = CharField(choices=...) Чоисы валидирует Django
Простые геттеры без логики Нет бизнес-логики

🎯 Критерии качества теста

Хороший тест

  • Изолированный — не зависит от других тестов
  • Детерминированный — одинаковый результат при каждом запуске
  • Быстрый — < 100ms на тест
  • Читаемый — понятно, что тестирует и почему
  • Минимальный — проверяет одно поведение
  • С mocking — внешние зависимости замоканы

Плохой тест

  • Зависит от порядка запуска
  • Использует реальные внешние сервисы
  • Проверяет несколько вещей одновременно
  • Требует долгих вычислений
  • Дублирует другой тест