Calidad del software y tipos de pruebas
"Testing puede mostrar la presencia de bugs, pero no puede demostrar su ausencia." — Edsger Dijkstra.
Qué vas a aprender en este capítulo
La calidad no es "que funcione". Es un conjunto de atributos medibles: funcionalidad, rendimiento, usabilidad, seguridad, mantenibilidad. Las pruebas de software son el mecanismo sistemático para verificar esos atributos. Este capítulo describe el panorama completo: qué tipos de pruebas existen, cuándo usarlos, y cómo diseñar buenos casos de prueba.
3.1 ¿Qué es la calidad del software?
💡 Intuición
Cuando un cliente dice "quiero buena calidad", no siempre dicen lo mismo. Un usuario de WhatsApp quiere que nunca falle y que sea rápido. Un banco quiere que jamás haya errores en los montos. Una app médica quiere que sea imposible cometer un error que dañe a un paciente.
La calidad tiene múltiples dimensiones, y distintas aplicaciones priorizan distintas dimensiones.
📐 Fundamento
Atributos de calidad ISO 25010:
| Atributo | Pregunta |
|---|---|
| Funcionalidad | ¿Hace lo que debe hacer? |
| Rendimiento | ¿Lo hace rápido y eficientemente? |
| Compatibilidad | ¿Convive con otros sistemas? |
| Usabilidad | ¿Es fácil de usar? |
| Fiabilidad | ¿Funciona sin fallar? |
| Seguridad | ¿Protege los datos? |
| Mantenibilidad | ¿Se puede cambiar fácilmente? |
| Portabilidad | ¿Funciona en distintos entornos? |
Métricas de calidad:
- Defect density: Bugs por 1,000 líneas de código (KLOC). < 1 bug/KLOC es excelente.
- Mean Time Between Failures (MTBF): Tiempo promedio entre fallos. Alto es mejor.
- Mean Time To Recover (MTTR): Tiempo promedio para recuperarse de un fallo. Bajo es mejor.
- Code coverage: % del código ejecutado por las pruebas automáticas. Objetivo: ≥80%.
- Technical Debt Ratio: Tiempo para pagar la deuda / costo de construcción.
- Customer Satisfaction Score (CSAT): Encuesta a usuarios. Subjetivo pero valioso.
3.2 La pirámide de testing
💡 Intuición
Mike Cohn propuso la pirámide de testing para describir la distribución ideal de tipos de pruebas:
/\
/ \ E2E (pocos, lentos, costosos)
/────\
/ \ Integración (medianos)
/────────\
/ \ Unitarias (muchas, rápidas, baratas)
/────────────\
Muchas pruebas unitarias — verifican una función o clase de forma aislada. Rápidas (millisegundos). Baratas de escribir y mantener. Fallan rápido cuando hay regresiones.
Algunas pruebas de integración — verifican que varios componentes funcionen juntos (ej: el servicio + la base de datos). Más lentas. Más difíciles de configurar.
Pocas pruebas E2E (end-to-end) — verifican el sistema completo desde la interfaz de usuario. Lentas (segundos o minutos cada una). Frágiles (cualquier cambio en la UI las rompe). Costosas de mantener.
El anti-patrón: el cono de helado — invertir la pirámide. Muchas pruebas E2E, pocas unitarias. Resultado: pruebas lentas, frágiles, difíciles de mantener, y cuando fallan no dicen qué está mal.
📐 Fundamento
Tipos de pruebas por alcance:
1. Pruebas unitarias (Unit Tests)
Verifican una única unidad de código (función, método, clase) de forma aislada. Las dependencias externas se reemplazan por mocks o stubs.
2. Pruebas de integración (Integration Tests)
Verifican que varios módulos funcionen correctamente juntos. Ej: el repositorio + la base de datos real (o de prueba).
3. Pruebas de sistema / E2E (End-to-End Tests)
Verifican el flujo completo del sistema desde la perspectiva del usuario. Usan herramientas como Selenium, Playwright, Cypress.
Pruebas por objetivo:
| Tipo | Objetivo |
|---|---|
| Funcionales | Verificar que el sistema hace lo especificado |
| No funcionales | Rendimiento, seguridad, usabilidad |
| De regresión | Verificar que cambios nuevos no rompieron lo que funcionaba |
| De humo (smoke) | Verificación rápida de que el sistema básico funciona tras un despliegue |
| De aceptación (UAT) | El cliente verifica que el sistema cumple sus expectativas |
Pruebas de caja negra vs caja blanca:
- Caja negra: El tester no conoce el código interno. Solo conoce entradas y salidas esperadas.
- Caja blanca: El tester conoce el código y diseña pruebas para cubrir caminos específicos.
3.3 Diseño de casos de prueba
💡 Intuición
No es posible probar todas las combinaciones posibles de entradas — son infinitas. Las técnicas de diseño de casos de prueba ayudan a seleccionar el mínimo de casos que maximice la cobertura.
Partición equivalente: Divide el espacio de entradas en clases donde se espera el mismo comportamiento. Si una entrada de la clase funciona, todas deberían funcionar (o fallar). Solo necesitás probar un representante de cada clase.
Valores límite (Boundary Value Analysis): Los bugs se concentran en los bordes. Si la función acepta , probá 0, 1, 2, 99, 100, 101.
📐 Fundamento
Partición equivalente:
Para una función calcular_descuento(monto) que:
- Devuelve error si
monto ≤ 0 - No aplica descuento si
0 < monto < 100 - Aplica 10% si
100 ≤ monto < 500 - Aplica 20% si
monto ≥ 500
Clases equivalentes:
monto ≤ 0(inválida)0 < monto < 100(válida, sin descuento)100 ≤ monto < 500(válida, 10%)monto ≥ 500(válida, 20%)
Casos de prueba mínimos: un representante por clase → 4 casos.
Valores límite (además de partición):
Para cada límite, probar el valor en el límite, uno antes y uno después:
- Límite 100: probar 99, 100, 101
- Límite 500: probar 499, 500, 501
Template de caso de prueba:
| Campo | Valor |
|---|---|
| ID | TC-001 |
| Historia/Req. | HU-005 |
| Descripción | Calcular descuento para monto en clase 10% |
| Precondición | Sistema iniciado |
| Datos de entrada | monto = 200 |
| Pasos | Llamar calcular_descuento(200) |
| Resultado esperado | 180.0 (200 - 20) |
| Resultado real | (a completar) |
| Estado | (a completar) |
🛠️ En la práctica
Casos de prueba para La Esquina — HU-003: Registrar pedido:
| ID | Descripción | Entrada | Resultado esperado |
|---|---|---|---|
| TC-001 | Pedido válido con un platillo | 1 pupusa de queso | Pedido creado, aparece en cocina |
| TC-002 | Pedido con múltiples platillos | 2 pupusas + 1 refresco | Pedido creado con 3 ítems |
| TC-003 | Pedido vacío (sin platillos) | 0 platillos | Error: "Agrega al menos un platillo" |
| TC-004 | Platillo sin stock | Platillo marcado como agotado | Error: "Platillo no disponible" |
| TC-005 | Cantidad = 0 | 0 pupusas | Error: "Cantidad debe ser mayor a 0" |
| TC-006 | Cantidad negativa | -1 pupusas | Error: "Cantidad inválida" |
| TC-007 | Dos pedidos simultáneos de la misma mesa | 2 mozos agregan pedidos a la vez | Ambos pedidos se registran sin conflicto |
Nota en TC-007: Este es un caso de prueba concurrente — difícil de hacer manualmente, más fácil con pruebas de integración automáticas.
3.4 El rol del QA en equipos ágiles
📐 Fundamento
En los equipos ágiles modernos, todos son responsables de la calidad, no solo el QA. El rol del QA ha evolucionado:
QA tradicional (waterfall):
- Prueba el software al final del ciclo.
- Encuentra bugs que ya son costosos de corregir.
- A veces es el "guardián" que aprueba o rechaza el release.
QA en equipos ágiles (SDET — Software Development Engineer in Test):
- Trabaja con el equipo desde el inicio del sprint.
- Participa en la refinamiento de historias (¿están los criterios de aceptación claros?).
- Escribe pruebas automáticas junto con los desarrolladores.
- Hace pruebas exploratorias (probar sin guión, buscando bugs inesperados).
- Evalúa la calidad continuamente, no al final.
Pruebas de aceptación en el sprint:
Las historias de usuario no están "Done" hasta que pasan las pruebas de aceptación. En Scrum, esto se hace antes de la Sprint Review:
- Desarrollador termina la implementación.
- QA ejecuta los casos de prueba (automatizados y manuales).
- Si hay defectos, vuelve al desarrollador.
- Solo cuando pasa, va a la Sprint Review con el PO.
3.5 Ejercicios
✏️ Ejercicio 3.1 — Partición equivalente
Una función validar_cedula(cedula) valida el número de DUI salvadoreño:
- Formato: 9 dígitos, un guión, 1 dígito verificador. Ej: "01234567-8"
- Solo acepta dígitos en las posiciones correctas.
Identificá las clases de equivalencia y diseñá un caso de prueba por clase.
Solución
Clases de equivalencia:
- Válida: 9 dígitos + guión + 1 dígito. Ej: "01234567-8" → VÁLIDA
- Longitud incorrecta (muy corta): "0123456-8" → INVÁLIDA
- Longitud incorrecta (muy larga): "012345678-90" → INVÁLIDA
- Sin guión: "0123456789" → INVÁLIDA
- Con letras: "0123456A-8" → INVÁLIDA
- Guión en posición incorrecta: "012345-678" → INVÁLIDA
- Cadena vacía: "" → INVÁLIDA
Casos de prueba (un representante por clase):
| ID | Entrada | Esperado |
|---|---|---|
| TC-1 | "01234567-8" | VÁLIDA |
| TC-2 | "0123456-8" | INVÁLIDA |
| TC-3 | "012345678-90" | INVÁLIDA |
| TC-4 | "0123456789" | INVÁLIDA |
| TC-5 | "0123456A-8" | INVÁLIDA |
| TC-6 | "012345-678" | INVÁLIDA |
| TC-7 | "" | INVÁLIDA |
✏️ Ejercicio 3.2 — Pirámide de testing
Para el sistema de La Esquina, propone ejemplos concretos de:
a. 3 pruebas unitarias. b. 2 pruebas de integración. c. 1 prueba E2E.
Para cada una, indica qué verifica y con qué herramienta la implementarías.
Solución
Pruebas unitarias (pytest en Python):
test_calcular_total_con_iva()— Dado un pedido con 3 pupusas a 3.39 (3 × 1.00 × 1.13). No necesita BD.test_pedido_vacio_lanza_excepcion()— Un pedido sin ítems lanzaPedidoVacioErroral intentar enviarse.test_estado_invalido_no_transiciona()— Un pedido "Cancelado" no puede pasar a "En preparación".
Pruebas de integración (pytest + base de datos de prueba):
test_guardar_y_recuperar_pedido()— Crear un pedido en la BD, recuperarlo por ID, verificar que todos los campos son iguales al original.test_envio_pedido_aparece_en_cocina()— Al enviar un pedido, aparece en el endpoint de cocina con los datos correctos.
Prueba E2E (Playwright o Selenium):
- "El mozo puede tomar un pedido completo" — Abrir el browser, iniciar sesión como mozo, seleccionar una mesa, agregar platillos, confirmar pedido, verificar que aparece en la pantalla de cocina.
3.6 Para profundizar
- Kaner, Falk & Nguyen, Testing Computer Software — clásico de pruebas de software.
- Myers, Sandler & Badgett, The Art of Software Testing — técnicas de diseño de casos.
- Siguiente: TDD y testing automatizado — escribir pruebas antes que el código.
Definiciones nuevas: calidad de software, ISO 25010, defect density, code coverage, pirámide de testing, prueba unitaria, prueba de integración, prueba E2E, prueba de regresión, prueba de humo, UAT, caja negra, caja blanca, partición equivalente, valores límite, SDET, prueba exploratoria.