раздел 05 · шаг 6/7

Шаг 6. Тесты

Покрытие бэка через pytest, фронта - через vitest + Testing Library. AI-вызов мокаем, чтобы не сжигать токены в CI. На выходе - coverage-отчёт.

Что делаем

  • Расширяем pytest для бэка: CRUD кейсы, edge cases, мок Anthropic
  • Поднимаем vitest на фронте
  • Пишем юнит и компонент-тесты для React
  • Подключаем coverage

Backend: тесты

Промпт для Claude

Расширь pytest-тесты в backend/tests/:

CRUD задач (tests/test_tasks_crud.py):
- POST с валидным title - 201, возвращается с id и created_at
- POST с пустым title - 422 (validation error)
- POST с title > 200 символов - 422
- GET /api/tasks - возвращает массив, сортировка created_at desc
- GET после нескольких POST - корректный порядок
- PATCH существующей задачи - меняет поля
- PATCH несуществующего id - 404
- PATCH с пустым body - возвращает задачу без изменений
- DELETE существующей - 204
- DELETE несуществующей - 404
- DELETE дважды - первая 204, вторая 404

AI-категоризация (tests/test_categorizer.py):
- Мок anthropic.AsyncAnthropic через monkeypatch
- categorize возвращает валидную категорию из мока - результат проходит
- Мок возвращает неизвестное слово - результат "other"
- Мок бросает APIError - результат "other", не падает
- Мок бросает TimeoutError - результат "other"
- В POST /api/tasks: проверяем что категория из мока попадает в БД

Используй:
- pytest-asyncio с asyncio_mode = "auto" в pyproject.toml или pytest.ini
- httpx.AsyncClient для интеграционных тестов
- pytest-mock для удобного monkeypatch (поставь pytest-mock в requirements)
- pytest-cov для coverage (тоже в requirements)

Скрипты в README:
- pytest -v - все тесты
- pytest --cov=app --cov-report=html - coverage в htmlcov/

Цель coverage: > 80% по app/

Запуск

source venv/bin/activate
cd backend
pip install -r requirements.txt
pytest -v
pytest --cov=app --cov-report=term --cov-report=html

Открыть отчёт: open htmlcov/index.html (на macOS).

Frontend: тесты

Промпт для Claude

Подключи vitest + Testing Library в frontend/:

Установи devDependencies:
- vitest
- @testing-library/react
- @testing-library/jest-dom
- @testing-library/user-event
- jsdom
- @vitest/coverage-v8

Конфиг vitest.config.ts:
- environment: jsdom
- setupFiles: ./src/test/setup.ts
- coverage с провайдером v8, report: text + html

В src/test/setup.ts:
- import "@testing-library/jest-dom/vitest"

Скрипты в package.json:
- "test": "vitest"
- "test:run": "vitest run"
- "test:coverage": "vitest run --coverage"

Тесты:
1. src/components/TaskInput.test.tsx
   - render: input есть в DOM
   - ввод текста + Enter вызывает onCreate с этим текстом
   - после успешного создания input очищается
   - в isPending input disabled

2. src/components/TaskItem.test.tsx
   - render: title виден, чекбокс отражает task.done
   - клик по чекбоксу вызывает onToggle с task.id
   - клик по кнопке удалить вызывает onDelete с task.id
   - бейдж категории показывает task.category

3. src/components/TaskList.test.tsx
   - loading: показывает skeleton (3 элемента)
   - error: показывает текст ошибки и кнопку Повторить
   - empty: показывает приглашение
   - filled: рендерит все задачи

Используй QueryClientProvider в обёртке (renderWithQuery утилита).
Мокай fetch через msw или vi.spyOn(global, 'fetch').

Запуск

cd frontend
pnpm install
pnpm test:run
pnpm test:coverage

Coverage-отчёт: frontend/coverage/index.html.

Что должно покрываться

Минимум для зелёного билда:

| Слой | Coverage цель | | --- | --- | | backend/app/routers/ | 90%+ | | backend/app/services/ | 80%+ | | backend/app/models/ | модели тестируются через роуты | | frontend/src/components/ | 75%+ | | frontend/src/lib/ | 90%+ |

Edge cases, про которые часто забывают

Если Claude в первой генерации их не покрыл - попросите явно:

  • Пустой title - не должен пройти валидацию
  • Очень длинный title - валидация на длину
  • category присланная клиентом игнорируется (вычисляется AI)
  • Двойной DELETE одного и того же id
  • PATCH с done: null - что делает приложение?
  • Параллельные POST - проверьте что id не коллидят (SQLite одного соединения этого не покажет, но проверка не помешает)

Что НЕ нужно тестировать

  • Сам факт того, что Anthropic API работает - это не наша зона ответственности
  • Реальные сетевые вызовы в CI - только моки
  • UI-снапшоты в TaskList - они хрупкие, лучше проверять конкретные элементы

Точка сохранения

cd ..
git add .
git commit -m "step 6: pytest for backend, vitest for frontend, coverage > 80%"

Полезные ссылки

  • pytest - официальная документация
  • Vitest - быстрый тест-раннер для Vite
  • Testing Library React - паттерны для React-тестов
  • MSW - моки сетевых запросов на уровне service worker