StackPractices
beginner Por Mathias Paulenko

Plantilla de caso de prueba con pasos y ejemplos

Un formato estandarizado de caso de prueba con pasos, resultados esperados, precondiciones y postcondiciones para testing manual y automatizado.

Temas: testing

Visión general

Un caso de prueba es un conjunto de condiciones bajo las cuales un tester verifica que una aplicación cumple un requisito. El formato importa menos que la disciplina detrás: todo caso de prueba debería ser ejecutable por alguien que no lo escribió, producir el mismo resultado en cualquier máquina y ser trazable hasta el requisito que verifica.

La plantilla de abajo cubre un flujo de login de punta a punta: metadata, precondiciones, ocho pasos con resultados esperados, postcondiciones, datos de prueba, seis casos negativos, cinco casos límite, una matriz de trazabilidad y una versión en Playwright del mismo caso. Copiala, reemplazá el ejemplo de login y funciona para casi cualquier feature. Un segundo escenario rellenado muestra cómo se ve la misma estructura para un endpoint de API.

Cuándo usarla

  • Escribir casos de prueba manuales para un ciclo de QA o una corrida de regresión
  • Producir documentación de testing que una auditoría de compliance va a leer (SOC 2, ISO 27001, FDA)
  • Incorporar miembros del equipo de QA a un estándar de testing compartido
  • Convertir un test manual en un script automatizado; la tabla de pasos se traduce casi 1:1 a código
  • Probar cobertura: vincular cada caso a un requisito muestra los huecos de un vistazo

Si lo que necesitás es la imagen completa — alcance, entornos, criterios de entrada y salida para todo el esfuerzo — eso es un documento de estrategia de testing, no un caso de prueba.

La plantilla

# Caso de Prueba: TC-<ID>

## Metadata

| Campo | Valor |
|-------|-------|
| ID de caso de prueba | TC-001 |
| Título | Verificar que el usuario puede iniciar sesión con credenciales válidas |
| Módulo | Autenticación |
| Feature | Login |
| Prioridad | Crítica |
| Tipo | Funcional |
| Tipo de ejecución | Manual / Automatizada |
| Tiempo estimado | 5 minutos |
| Creado por | <Autor> |
| Fecha de creación | 2026-07-05 |
| Última actualización | 2026-07-05 |
| ID de requisito | REQ-AUTH-001 |
| Estado del test | Not Run / Pass / Fail / Blocked |

## Precondiciones

1. Existe una cuenta de usuario en el sistema con email `testuser@example.com`
2. La cuenta está activa (no suspendida ni eliminada)
3. El usuario conoce la contraseña correcta: `Test@1234`
4. La aplicación es accesible en `https://app.example.com`
5. El navegador es Chrome 120+ o Firefox 120+
6. No hay sesión activa para el usuario de prueba (sesión cerrada)

## Pasos del test

| Paso # | Acción | Resultado esperado | Resultado real | Estado |
|--------|--------|--------------------|----------------|--------|
| 1 | Navegar a `https://app.example.com/login` | La página de login carga con campos de email y contraseña, y un botón "Log In" | | |
| 2 | Ingresar `testuser@example.com` en el campo de email | El campo de email muestra el valor ingresado | | |
| 3 | Ingresar `Test@1234` en el campo de contraseña | El campo muestra caracteres enmascarados (••••••••) | | |
| 4 | Hacer clic en el botón "Log In" | El sistema procesa la solicitud de login | | |
| 5 | Esperar la redirección | El usuario es redirigido al dashboard en `/dashboard` | | |
| 6 | Verificar el avatar del usuario | El avatar con iniciales "TU" es visible en la esquina superior derecha | | |
| 7 | Verificar el mensaje de bienvenida | El dashboard muestra "Welcome, Test User" | | |
| 8 | Verificar la cookie de sesión | Existe una cookie `session_id` con los flags `HttpOnly` y `Secure` | | |

## Postcondiciones

1. El usuario está autenticado y tiene una sesión activa
2. El token de sesión es válido por 24 horas
3. El log de actividad registra el evento de login con timestamp e IP
4. El usuario está en la página del dashboard

## Datos de prueba

| Campo | Valor | Notas |
|-------|-------|-------|
| Email | `testuser@example.com` | Cuenta de prueba precreada |
| Contraseña | `Test@1234` | Cambia cada trimestre |
| Email inválido | `invalid@example.com` | Para caso de prueba negativo |
| Contraseña inválida | `wrongpass` | Para caso de prueba negativo |

## Casos de prueba negativos

| ID de caso | Descripción | Pasos | Resultado esperado |
|------------|-------------|-------|--------------------|
| TC-001-N1 | Login con contraseña inválida | Igual que TC-001 pero ingresar `wrongpass` en el paso 3 | Se muestra el error "Invalid email or password". El usuario permanece en la página de login. |
| TC-001-N2 | Login con email inexistente | Igual que TC-001 pero ingresar `nobody@example.com` en el paso 2 | Se muestra "Invalid email or password". Sin enumeración de cuentas. |
| TC-001-N3 | Login con campo de email vacío | Omitir el paso 2, hacer clic en "Log In" | El campo de email muestra el error de validación "Email is required" |
| TC-001-N4 | Login con campo de contraseña vacío | Omitir el paso 3, hacer clic en "Log In" | El campo muestra el error "Password is required" |
| TC-001-N5 | Login con SQL injection en el email | Ingresar `' OR 1=1; --` en el campo de email | Se muestra "Invalid email format". Sin ejecución de SQL. |
| TC-001-N6 | Login después de 5 intentos fallidos | Repetir TC-001-N1 cinco veces | La cuenta se bloquea por 15 minutos. Error "Account locked due to too many attempts." |

## Casos límite

| ID de caso | Descripción | Resultado esperado |
|------------|-------------|--------------------|
| TC-001-E1 | Email con espacios al inicio/final | `  testuser@example.com  ` debería recortarse y aceptarse |
| TC-001-E2 | Email en mayúsculas | `TestUser@Example.com` debería aceptarse sin importar mayúsculas |
| TC-001-E3 | Contraseña con caracteres especiales | `Tëst@1234!#$%` debería aceptarse si cumple la política |
| TC-001-E4 | Timeout de sesión durante el login | Si el login tarda > 30s, mostrar "Request timeout. Please try again." |
| TC-001-E5 | Login concurrente desde dos navegadores | El segundo login debería funcionar. La primera sesión queda invalidada. |

## Trazabilidad

| ID de requisito | Descripción del requisito | IDs de casos |
|-----------------|---------------------------|--------------|
| REQ-AUTH-001 | El usuario puede iniciar sesión con credenciales válidas | TC-001 |
| REQ-AUTH-002 | Credenciales inválidas muestran error sin enumeración | TC-001-N1, TC-001-N2 |
| REQ-AUTH-003 | La cuenta se bloquea tras 5 intentos fallidos | TC-001-N6 |
| REQ-AUTH-004 | La sesión se invalida en login concurrente | TC-001-E5 |

## Notas de automatización

Si automatizás este caso de prueba con Playwright:

```typescript
test('TC-001: User can log in with valid credentials', async ({ page }) => {
  // Precondiciones: asegurar que el usuario de prueba existe
  await ensureTestUserExists();

  // Paso 1: navegar a la página de login
  await page.goto('https://app.example.com/login');
  await expect(page.locator('[data-testid="email-input"]')).toBeVisible();
  await expect(page.locator('[data-testid="password-input"]')).toBeVisible();
  await expect(page.locator('[data-testid="login-button"]')).toBeVisible();

  // Pasos 2-3: ingresar credenciales
  await page.fill('[data-testid="email-input"]', 'testuser@example.com');
  await page.fill('[data-testid="password-input"]', 'Test@1234');

  // Paso 4: clic en login
  await page.click('[data-testid="login-button"]');

  // Paso 5: verificar redirección al dashboard
  await expect(page).toHaveURL(/\/dashboard/);

  // Paso 6: verificar avatar del usuario
  await expect(page.locator('[data-testid="user-avatar"]')).toHaveText('TU');

  // Paso 7: verificar mensaje de bienvenida
  await expect(page.locator('[data-testid="welcome-message"]')).toHaveText('Welcome, Test User');

  // Paso 8: verificar cookie de sesión
  const cookies = await page.context().cookies();
  const sessionCookie = cookies.find(c => c.name === 'session_id');
  expect(sessionCookie).toBeDefined();
  expect(sessionCookie.httpOnly).toBe(true);
  expect(sessionCookie.secure).toBe(true);

  // Postcondición: limpieza
  await page.click('[data-testid="logout-button"]');
});

## Explicación

La plantilla tiene seis secciones clave. **Metadata** identifica el caso y lo vincula a un requisito. **Precondiciones** fijan el estado inicial. Saltatelas y el mismo test pasa en tu máquina y falla en la de un colega, sin razón visible. **Pasos** empareja una acción con un resultado esperado por fila; un paso con dos acciones o una expectativa vaga ("la página carga correctamente") no es verificable. **Postcondiciones** verifican el estado después del test, no solo el resultado inmediato. **Datos de prueba** mantiene los inputs en un solo lugar, así una rotación de contraseña toca una tabla en vez de veinte pasos. **Casos negativos y límite** viven en el mismo documento que el happy path; ahí están los bugs de verdad, y archivarlos aparte es cómo se terminan salteando.

La matriz de trazabilidad es la sección que los equipos sueltan primero y lamentan después. Es evidencia obligatoria para auditorías de compliance, pero rinde igual sin auditores: un requisito sin caso de prueba es un requisito que nadie verificó. Si generás reportes de cobertura, esta matriz es lo que los alimenta; mirá la [plantilla de reporte de cobertura](/es/docs/test-coverage-report-template/) para el lado del reporting.

### Escenario detallado: caso de prueba para API de creación de pedidos

La misma estructura funciona para testing de API: cambiá interacciones de UI por requests y estado del DOM por códigos de estado y payloads:

```text
API: POST /v1/orders
Módulo: Orders
Prioridad: Crítica
Tipo: Funcional + Integración

Precondiciones:
  1. Servicio de orders corriendo en staging
  2. Usuario autenticado con token JWT válido
  3. Al menos 3 productos con stock disponible en BD
  4. Dirección de envío válida creada previamente

Pasos:
  | # | Acción | Resultado esperado |
  |---|--------|--------------------|
  | 1 | POST /v1/orders con body válido | 201 Created con body {id, status, total} |
  | 2 | Verificar header Location | Contiene /v1/orders/{id} |
  | 3 | GET /v1/orders/{id} | 200 OK con el mismo body |
  | 4 | Verificar stock descontado | GET /v1/products/{sku} muestra stock -1 |
  | 5 | Verificar evento publicado | La cola de mensajes tiene el evento order.created |
  | 6 | Repetir POST con el mismo Idempotency-Key | 200 OK con el mismo pedido (sin duplicado) |

Casos negativos:
  | ID | Descripción | Resultado |
  |----|-------------|-----------|
  | N1 | customer_id inexistente | 400 con VALIDATION_ERROR |
  | N2 | Array de items vacío | 422 con EMPTY_ITEMS |
  | N3 | quantity <= 0 | 422 con INVALID_QUANTITY |
  | N4 | SKU sin stock | 409 con OUT_OF_STOCK |
  | N5 | Sin header Authorization | 401 Unauthorized |
  | N6 | Idempotency-Key duplicado con body distinto | 409 Conflict |

Casos límite:
  | ID | Descripción | Resultado |
  |----|-------------|-----------|
  | E1 | Pedido con 100 items | 201 Created, latencia < 2s |
  | E2 | Pedido con caracteres unicode en la dirección | 201 Created, datos preservados |
  | E3 | quantity = 999999 | 201 Created o 422 si excede el máximo permitido |
  | E4 | Concurrencia: 2 pedidos del mismo SKU a la vez | Uno pasa, el otro 409 OUT_OF_STOCK |

Trazabilidad:
  | Requisito | Casos |
  |-----------|-------|
  | REQ-ORD-001 | TC-ORD-001 (happy path) |
  | REQ-ORD-002 | TC-ORD-N1..N6 (validación) |
  | REQ-ORD-003 | TC-ORD-E4 (concurrencia) |
  | REQ-ORD-004 | TC-ORD-001 paso 6 (idempotencia) |

Mirá el paso 6: la idempotencia es un caso de primera clase para cualquier POST que crea recursos, y el negativo N6 (misma key, body distinto → 409) es el que más seguido falla en las implementaciones reales.

¿Cómo documento casos de prueba para flujos asíncronos?

Para flujos asíncronos (colas de mensajes, webhooks, eventos), documentá el evento esperado como resultado en lugar de una respuesta HTTP inmediata. Verificá tres cosas: el mensaje publicado en la cola, el estado final tras el procesamiento y la espera máxima aceptable. En automatización, hacé polling con retry — chequeá cada 500ms hasta un techo de 10s — en vez de dormir una duración fija.

¿Debería usar BDD (Gherkin) para escribir casos de prueba?

BDD (Given-When-Then) rinde cuando product managers o stakeholders tienen que leer los casos. Para equipos técnicos, el formato tabular es más directo. Si corrés Cucumber o Behave, Gherkin es obligatorio. Para Playwright o Vitest, la tabla mapea directo sobre código. Quedate con la tabla.

Cómo adaptar esta plantilla

Convenciones de IDs. TC-001 para el happy path, TC-001-N* para negativos, TC-001-E* para casos límite hace que cada caso sea direccionable en una pulsación. Prefijá por módulo (TC-AUTH-*, TC-ORD-*) cuando superás unas decenas de casos; la numeración plana no escala más allá de uno o dos sprints.

Priorizá por riesgo, no por pantalla. Un flujo de pagos necesita negativos y casos límite detallados; una feature de “cambiar color del avatar” necesita el happy path y uno o dos negativos como mucho. La matriz de la plantilla escala en ambos sentidos. La disciplina es ajustar la profundidad al radio de explosión, no rellenar todas las secciones para cada feature.

Campos que podés soltar. Tiempo estimado importa para corridas manuales de regresión y puede irse. Tipo de ejecución puede irse cuando la suite está totalmente automatizada. ID de requisito solo puede irse si tenés otro mecanismo de trazabilidad. No lo sueltes solo porque vincular es tedioso; así empiezan los huecos de cobertura.

Campos que vale la pena agregar según tu contexto. Entorno (staging vs prod) para QA entre entornos. ID de defecto vinculando al bug report cuando un caso falla; la plantilla de pasos de reproducción de bugs combina bien acá. Suite de regresión como flag sí/no si mantenés un set de smoke aparte.

De manual a automatizado. No automatices todo de una vez. Candidatos primero: casos que corren cada sprint, tocan rutas críticas y no dependen de juicio humano. La tabla de pasos de esta plantilla se traduce fila por fila: el ejemplo de Playwright de arriba son los mismos ocho pasos como código. Cuando un paso manual dice “verificar que el layout se ve bien”, eso es una aserción visual o una revisión humana, no un selector; automatizarlo a ciegas es de donde vienen los tests flaky, y los tests flaky son donde las precondiciones de la plantilla reciben la culpa primero.

Criterios de entrada y salida

Un caso de prueba documenta una verificación; un ciclo de QA documenta muchas. Definí cuándo escribir y ejecutar casos cuenta como terminado, o el ciclo se estira para siempre.

Antes de escribir un caso (entrada): el requisito está cerrado y revisado, existen criterios de aceptación, y los datos de prueba están disponibles o se pueden crear. Escribir casos contra requisitos que siguen moviéndose produce retrabajo, no cobertura.

Antes de arrancar una corrida (entrada): el entorno está arriba, el build bajo test está desplegado y las precondiciones de cada caso se pueden cumplir de verdad — una suite que no puede preparar su propio estado inicial se frena el primer día.

Para cerrar el ciclo (salida): el 100% de los casos planificados ejecutados, todos los casos críticos y de alta prioridad pasando, defectos clasificados por severidad, y el reporte de cobertura completado. Los casos bloqueados llevan una razón, no un salto silencioso.

Variantes

ContextoEnfoqueNotas
Testing de APIReemplazar pasos de UI por requests HTTPUsar códigos de estado y aserciones sobre el body (ver el ejemplo de pedidos)
Testing mobileAgregar dispositivo/OS a precondicionesProbar al menos en viewports chicos y grandes
Testing de performanceAgregar parámetros de carga y umbrales de latenciaUsar scripts de k6 o JMeter
Testing de complianceAgregar mapeo de requisitos regulatoriosFDA 21 CFR Part 11, GDPR
Testing exploratorioUsar formato charter en vez de pasosExploración con timebox y debrief

Las sesiones exploratorias no encajan en esta plantilla. Escribí un charter (misión, alcance, timebox) y rellená un log de hallazgos en vez de pasos. La estructura de arriba es para verificación repetible, no para descubrimiento.

Lo que funciona

  1. Escribí las precondiciones explícitamente: “el usuario está logueado” es un estado, escribilo, o la próxima persona corre el caso contra el punto de partida equivocado
  2. Mantené los casos negativos y límite en el mismo documento que el happy path; los archivos separados se olvidan
  3. Usá atributos data-testid en automatización; son más estables que selectores CSS
  4. Vinculá cada caso a un requisito: nada de tests huérfanos
  5. Mantené los datos de prueba en una tabla compartida, no hardcodeados paso a paso
  6. Revisá los casos de prueba como código; el peer review detecta pasos faltantes y expectativas vagas
  7. Versioná los casos junto con los requisitos; cuando el requisito cambia, el caso debería cambiar en el mismo commit

Errores comunes

  1. Resultados esperados vagos (“la página carga correctamente” — ¿correctamente cómo, exactamente?)
  2. Precondiciones faltantes: “asume que el usuario está logueado” no es una precondición; escribí el estado explícito
  3. Solo happy path: los negativos son donde se esconden los bugs
  4. Datos de prueba hardcodeados en los pasos: una tabla de datos, o vas a editar quince casos cuando rote la contraseña
  5. Sin trazabilidad a requisitos: no podés probar cobertura en una auditoría ni encontrar los huecos vos mismo
  6. Detalle de script en casos automatizados: el código es la documentación; el caso solo necesita intención y aserciones
  7. Nunca actualizar los casos: un caso de prueba viejo cuesta más tiempo que no tener ninguno, porque alguien confía en él
  8. Copiar toda la plantilla para una feature trivial: quedate con los campos que se ganan su lugar; un login y un selector de color no merecen la misma profundidad

Ver también

Preguntas frecuentes

¿Qué nivel de detalle deberían tener los pasos?

El suficiente para que alguien que nunca vio la feature pueda correr el test y verificar el resultado. Una acción, un resultado esperado por fila; si un paso tiene dos acciones, dividilo. Para tests automatizados el código es la documentación; los pasos son para ejecución manual.

¿Debería escribir los casos de prueba antes o después del desarrollo?

Antes. Los casos de prueba salen de los requisitos, no de la implementación. Escribirlos junto con los criterios de aceptación detecta ambigüedades mientras todavía son baratas; "¿qué debería pasar con un email duplicado?" es una pregunta de requisitos, no un bug report.

¿Cómo manejo casos de prueba que cambian seguido?

Parameterizá. Mantené los pasos estables y poné lo que varía en la tabla de datos; un caso de login con diez combinaciones de credenciales en los datos de prueba le gana a diez casos separados que vas a olvidar actualizar.

¿Cuál es la diferencia entre un caso de prueba y un escenario de prueba?

El escenario es el qué ("verificar que el login funciona"); el caso es el cómo (ocho pasos con inputs específicos y resultados esperados). Un escenario suele abanicar en varios casos: happy path, negativos, casos límite.

¿Cuántos casos de prueba por feature?

Ajustá la profundidad al riesgo. Una feature simple necesita de cinco a diez casos (happy path, tres a cinco negativos, un par de casos límite); flujos de pagos o auth pueden justificar cincuenta o más. No escribas casos para cambios triviales de UI; el tiempo de revisión es finito.