Módulo 8: Proyecto Integrador — Test Suite Completa con TDD
Planning Spec-First: Diseñar desde los Tests
Planning Spec-First: Diseñar desde los Tests
Descripción de la cápsula
Antes de escribir una línea de implementación, vas a diseñar la TaskFlow API desde los tests. Esta cápsula es pura planificación: descomponer features en user stories, mapear cada story a specs testeables, definir los nombres exactos de los tests que escribirás, y crear la estructura de archivos vacía.
No implementas nada aquí. Diseñas el contrato entre lo que la aplicación debe hacer y cómo lo validarás. Al terminar, tendrás un blueprint completo que guía todo el desarrollo posterior.
¿Por Qué Planning Antes de Código?
El problema de "empezar a codear"
Cuando empiezas a implementar sin planificación, tiendes a:
- Escribir features que no cubren todos los casos necesarios
- Descubrir requirements a mitad de camino
- Terminar con tests que no reflejan el comportamiento esperado
- Perder tiempo refactorizando porque la arquitectura no anticipó ciertos casos
El enfoque spec-first
En spec-first, defines los tests como especificación antes de implementar:
- Los tests son el contrato: "la app debe hacer X, Y, Z"
- Claude Code implementa contra ese contrato
- No hay ambigüedad: el test pasa o falla
- La feature decomposition te obliga a pensar en edge cases antes de escribir código
Feature Decomposition: Romper Features en Specs
Qué es feature decomposition
Feature decomposition es dividir un feature grande ("autenticación") en specs individuales y testeables. Cada spec es un test con un nombre que describe exactamente qué debe pasar.
Ejemplo:
Feature grande: "Autenticación"
→ Spec 1: register con email válido crea usuario
→ Spec 2: register con email duplicado falla
→ Spec 3: login con credenciales correctas retorna token
→ Spec 4: login con password incorrecto falla
...
Cómo decomponer
Para cada feature, pregúntate:
- ¿Cuál es el happy path?
- ¿Qué errores pueden ocurrir?
- ¿Qué edge cases existen?
- ¿Qué validaciones deben aplicarse?
Cada respuesta se convierte en el nombre de un test.
User Stories → Test Specs: El Mapeo
Feature 1: Autenticación
User stories:
- Como usuario, quiero registrarme con email y password para tener una cuenta
- Como usuario, quiero iniciar sesión con mis credenciales para obtener un token
- Como usuario, quiero que mi token sea válido para acceder a endpoints protegidos
- Como usuario, quiero que credenciales inválidas sean rechazadas
Test specs (8-10 tests):
| # | Nombre del test | Qué valida |
|---|---|---|
| 1 | test_register_creates_user_with_valid_email_and_password | Registro exitoso crea usuario y retorna datos (sin password) |
| 2 | test_register_duplicate_email_fails | Email ya registrado → ValueError |
| 3 | test_register_invalid_email_raises | Email mal formado → ValueError |
| 4 | test_register_weak_password_raises | Password que no cumple requisitos → ValueError |
| 5 | test_login_returns_token_for_valid_credentials | Login exitoso retorna token string |
| 6 | test_login_wrong_password_raises | Password incorrecto → ValueError |
| 7 | test_login_nonexistent_user_raises | Usuario no existe → ValueError |
| 8 | test_validate_token_returns_user_for_valid_token | Token válido retorna datos del usuario |
| 9 | test_validate_token_raises_for_invalid_token | Token inválido o expirado → ValueError |
| 10 | test_password_is_hashed_on_register | El password nunca se almacena en texto plano |
Feature 2: Equipos (Teams)
User stories:
- Como usuario, quiero crear un equipo para organizar tareas con otros
- Como owner, quiero agregar miembros a mi equipo
- Como usuario, quiero listar los equipos a los que pertenezco
Test specs (6-8 tests):
| # | Nombre del test | Qué valida |
|---|---|---|
| 1 | test_create_team_returns_team_with_owner | Crear equipo retorna equipo con el creator como owner |
| 2 | test_create_team_requires_authenticated_user | Sin token → 401 |
| 3 | test_add_member_adds_user_to_team | Agregar miembro exitosamente |
| 4 | test_add_member_requires_owner_permission | Solo owner puede agregar miembros |
| 5 | test_add_member_duplicate_raises | Agregar miembro ya existente → error |
| 6 | test_list_teams_returns_user_teams | Listar equipos retorna solo los del usuario |
| 7 | test_list_team_members_returns_correct_users | Listar miembros retorna owner + miembros |
| 8 | test_get_team_by_id_returns_team_or_404 | Obtener equipo por ID, 404 si no existe |
Feature 3: Tareas (Tasks)
User stories:
- Como usuario, quiero crear tareas dentro de un equipo
- Como usuario, quiero asignar tareas a miembros del equipo
- Como usuario, quiero cambiar el estado de las tareas (pendiente → en progreso → completada)
- Como usuario, quiero filtrar tareas por estado, prioridad y asignee
Test specs (10-12 tests):
| # | Nombre del test | Qué valida |
|---|---|---|
| 1 | test_create_task_returns_task_in_team | Crear tarea retorna tarea con datos correctos |
| 2 | test_create_task_requires_team_membership | Usuario no miembro no puede crear tarea |
| 3 | test_get_task_by_id_returns_task_or_404 | Obtener tarea por ID |
| 4 | test_update_task_modifies_fields | Actualizar título, descripción, etc. |
| 5 | test_delete_task_removes_from_team | Eliminar tarea la quita del equipo |
| 6 | test_assign_task_to_team_member_succeeds | Asignar tarea a miembro del equipo |
| 7 | test_assign_task_to_non_member_fails | Asignar a no-miembro falla |
| 8 | test_transition_pending_to_in_progress_succeeds | Transición válida pending → in_progress |
| 9 | test_transition_in_progress_to_completed_succeeds | Transición válida in_progress → completed |
| 10 | test_filter_tasks_by_status_returns_matching | Filtrar por status retorna solo las que coinciden |
| 11 | test_filter_tasks_by_priority_returns_matching | Filtrar por prioridad |
| 12 | test_filter_tasks_by_assignee_returns_matching | Filtrar por asignee |
Feature 4: Business Logic
User stories:
- Como sistema, debo validar que las transiciones de estado sean válidas (no retroceder)
- Como sistema, debo validar permisos: solo el asignee puede completar una tarea
- Como usuario, quiero ver estadísticas del equipo (tareas por estado, tasa de completación)
Test specs (6-8 tests):
| # | Nombre del test | Qué valida |
|---|---|---|
| 1 | test_transition_completed_to_in_progress_raises | No se puede retroceder completed → in_progress |
| 2 | test_transition_pending_to_completed_raises | No se puede saltar pending → completed (debe pasar por in_progress) |
| 3 | test_only_assigned_user_can_complete_task | Solo el asignee puede marcar como completed |
| 4 | test_team_owner_can_add_members | Permiso de owner para agregar |
| 5 | test_get_team_stats_returns_tasks_by_status | Stats retornan conteo por status |
| 6 | test_get_team_stats_completion_rate | Completation rate calculado correctamente |
| 7 | test_unassigned_task_can_be_completed_by_any_member | Tarea sin asignee: cualquier miembro puede completar |
| 8 | test_invalid_status_transition_raises | Transición a status inválido → ValueError |
Crear la Estructura de Tests (Archivos Vacíos)
Estructura de directorios
taskflow/
├── app/
│ └── ... (estructura de la app)
├── tests/
│ ├── conftest.py
│ ├── unit/
│ │ ├── conftest.py
│ │ ├── test_auth.py
│ │ ├── test_teams.py
│ │ ├── test_tasks.py
│ │ └── test_rules.py
│ ├── integration/
│ │ ├── conftest.py
│ │ ├── test_auth_endpoints.py
│ │ ├── test_team_endpoints.py
│ │ └── test_task_endpoints.py
│ └── e2e/
│ ├── conftest.py
│ └── test_flows.py
Archivo: tests/unit/test_auth.py (estructura vacía)
# tests/unit/test_auth.py
# Tests para auth service (registro, login, validación de token)
def test_register_creates_user_with_valid_email_and_password():
pass
def test_register_duplicate_email_fails():
pass
def test_register_invalid_email_raises():
pass
def test_register_weak_password_raises():
pass
def test_login_returns_token_for_valid_credentials():
pass
def test_login_wrong_password_raises():
pass
def test_login_nonexistent_user_raises():
pass
def test_validate_token_returns_user_for_valid_token():
pass
def test_validate_token_raises_for_invalid_token():
pass
def test_password_is_hashed_on_register():
pass
Archivo: tests/unit/test_teams.py
# tests/unit/test_teams.py
def test_create_team_returns_team_with_owner():
pass
def test_add_member_adds_user_to_team():
pass
def test_add_member_requires_owner_permission():
pass
def test_add_member_duplicate_raises():
pass
def test_list_teams_returns_user_teams():
pass
def test_list_team_members_returns_correct_users():
pass
def test_get_team_by_id_returns_team_or_404():
pass
Archivo: tests/unit/test_tasks.py
# tests/unit/test_tasks.py
def test_create_task_returns_task_in_team():
pass
def test_create_task_requires_team_membership():
pass
def test_get_task_by_id_returns_task_or_404():
pass
def test_update_task_modifies_fields():
pass
def test_delete_task_removes_from_team():
pass
def test_assign_task_to_team_member_succeeds():
pass
def test_assign_task_to_non_member_fails():
pass
def test_filter_tasks_by_status_returns_matching():
pass
def test_filter_tasks_by_priority_returns_matching():
pass
def test_filter_tasks_by_assignee_returns_matching():
pass
Archivo: tests/unit/test_rules.py
# tests/unit/test_rules.py
# Business logic: transiciones de estado, permisos, stats
def test_transition_completed_to_in_progress_raises():
pass
def test_transition_pending_to_completed_raises():
pass
def test_only_assigned_user_can_complete_task():
pass
def test_get_team_stats_returns_tasks_by_status():
pass
def test_get_team_stats_completion_rate():
pass
def test_unassigned_task_can_be_completed_by_any_member():
pass
def test_invalid_status_transition_raises():
pass
Archivos de integration (estructura)
Para integration, los tests validan endpoints con TestClient. Los nombres reflejan el comportamiento HTTP:
test_register_endpoint_returns_201_and_usertest_login_endpoint_returns_200_and_tokentest_protected_endpoint_returns_401_without_tokentest_create_team_endpoint_returns_201test_add_member_endpoint_returns_200test_create_task_endpoint_returns_201- etc.
(En la próxima cápsula implementarás estos con código real.)
Priorización: Core First, Business Logic Después
Orden recomendado
No implementas todos los tests en paralelo. Sigues un orden que minimiza dependencias:
Fase 1 — Core (primero):
- Auth (todo el módulo): sin auth no hay usuarios, sin usuarios no hay equipos
- Teams (crear, listar): sin equipos no hay tareas
- Tasks (CRUD básico): crear, leer, actualizar, eliminar
Fase 2 — Business logic:
- Transiciones de estado
- Permisos (quién puede hacer qué)
- Stats y filtros
- Edge cases
Por qué este orden
- Auth es la base: cada endpoint protegido depende del token
- Teams es la segunda capa: las tareas pertenecen a equipos
- Tasks CRUD es el núcleo funcional: sin esto la app no hace nada útil
- Business logic refina el comportamiento pero presupone que el CRUD existe
Planning Checklist
Antes de pasar a la implementación, verifica que tienes:
- Lista completa de tests por feature (auth, teams, tasks, business logic)
- Archivos de test creados con
passy nombres descriptivos - Orden de implementación definido (auth → teams → tasks → rules)
- Claridad sobre qué tests son unit vs integration vs E2E
Resumen
- La feature decomposition convierte "quiero auth" en 10 specs concretos
- Cada user story se mapea a tests con nombres que documentan el comportamiento
- Creas la estructura de archivos vacía antes de implementar
- Priorizas: core features (auth, teams, tasks CRUD) primero; business logic después
- Esta cápsula es solo planificación — en la siguiente empieza el TDD real
Próxima cápsula: Core Features con TDD — implementar auth, teams y tasks ciclo por ciclo.
Módulo 8, Cápsula 02 — Testing with Claude Code Guide