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 [1,100][1, 100], 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:

  1. monto ≤ 0 (inválida)
  2. 0 < monto < 100 (válida, sin descuento)
  3. 100 ≤ monto < 500 (válida, 10%)
  4. 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:

  1. Desarrollador termina la implementación.
  2. QA ejecuta los casos de prueba (automatizados y manuales).
  3. Si hay defectos, vuelve al desarrollador.
  4. 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.

✏️ 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.


3.6 Para profundizar


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.