El testing de software se organiza en niveles distintos, cada uno dirigido a diferentes aspectos del sistema e involucrando a diferentes miembros del equipo. Comprender los niveles de testing ayuda a los equipos a estructurar su estrategia de testing, asignar responsabilidades y garantizar una cobertura de calidad integral desde componentes individuales hasta sistemas completos.
¿Qué Son los Niveles de Testing?
Los niveles de testing representan etapas en el ciclo de vida de desarrollo de software donde ocurren actividades de testing. Cada nivel tiene objetivos específicos, base de pruebas, objetos de prueba y defectos típicos para encontrar.
Los Cuatro Niveles Principales de Testing
┌─────────────────────────────────────────────┐
│ User Acceptance Testing (UAT) │ ← Negocio valida requisitos
├─────────────────────────────────────────────┤
│ System Testing │ ← Comportamiento del sistema completo
├─────────────────────────────────────────────┤
│ Integration Testing │ ← Interacciones entre componentes
├─────────────────────────────────────────────┤
│ Unit Testing │ ← Componentes individuales
└─────────────────────────────────────────────┘
Cada nivel se construye sobre el anterior, con defectos idealmente detectados en la etapa más temprana posible para minimizar costo y complejidad. Para una comprensión profunda de los diferentes enfoques de testing a través de estos niveles, consulta nuestra comparación de enfoques de testing.
Unit Testing
El unit testing verifica componentes individuales (funciones, métodos, clases) de forma aislada. Esta es la base de la pirámide de testing.
Objetivos
- Verificar que cada unidad funciona según diseño
- Detectar errores de lógica temprano en desarrollo
- Habilitar refactoring seguro mediante detección de regresiones
- Documentar comportamiento esperado de unidades de código
- Proporcionar feedback rápido a desarrolladores
Base de Pruebas
- Documentos de diseño detallado
- Implementación de código
- Especificaciones de componentes
- Documentación de API
¿Quién Realiza Unit Testing?
Principalmente desarrolladores, a menudo usando Test-Driven Development (TDD):
# Ejemplo: Unit test para servicio de autenticación
import pytest
from auth_service import AuthService, InvalidCredentialsError
class TestAuthService:
def setup_method(self):
"""Configurar fixtures antes de cada test"""
self.auth = AuthService(database="test_db")
(como se discute en [Entry and Exit Criteria in Software Testing: When to Start and Stop Testing](/blog/entry-exit-criteria)) self.valid_user = {
"email": "test@example.com",
"password": "SecurePass123!"
}
def test_successful_authentication(self):
"""Test de que credenciales válidas retornan auth token"""
token = self.auth.authenticate(
self.valid_user["email"],
self.valid_user["password"]
)
assert token is not None
assert len(token) == 64 # Longitud de JWT token
assert self.auth.is_token_valid(token) is True
def test_invalid_password_raises_error(self):
"""Test de que contraseña inválida genera error apropiado"""
with pytest.raises(InvalidCredentialsError) as exc_info:
self.auth.authenticate(
self.valid_user["email"],
"WrongPassword"
)
assert "Invalid credentials" in str(exc_info.value)
def test_nonexistent_user_raises_error(self):
"""Test de que usuario no existente genera error"""
with pytest.raises(InvalidCredentialsError):
self.auth.authenticate(
"nonexistent@example.com",
"anypassword"
)
def test_account_lockout_after_failed_attempts(self):
"""Test de que cuenta se bloquea después de 5 intentos fallidos"""
# Intentar 5 logins fallidos
for _ in range(5):
with pytest.raises(InvalidCredentialsError):
self.auth.authenticate(
self.valid_user["email"],
"WrongPassword"
)
# 6to intento debe generar AccountLockedError
from auth_service import AccountLockedError
with pytest.raises(AccountLockedError):
self.auth.authenticate(
self.valid_user["email"],
self.valid_user["password"] # Incluso con contraseña correcta
)
def test_token_expiration(self):
"""Test de que tokens expiran después de tiempo configurado"""
import time
token = self.auth.authenticate(
self.valid_user["email"],
self.valid_user["password"]
)
# Simular paso de tiempo (en código real, usar librería de time mocking)
self.auth._set_time_offset(3600) # +1 hora
assert self.auth.is_token_valid(token) is False
Defectos Típicos Encontrados
- Cálculos o lógica incorrecta
- Errores de valores límite
- Excepciones de puntero nulo
- Tipos de variable incorrectos
- Errores de bucle (off-by-one, bucles infinitos)
- Manejo de errores incorrecto
Mejores Prácticas
- Seguir patrón AAA: Arrange, Act, Assert
- Probar una cosa por test: Cada test debe verificar un comportamiento único
- Usar nombres descriptivos:
test_account_locks_after_5_failed_attempts
- Aislar dependencias: Usar mocks/stubs para dependencias externas
- Apuntar a alta cobertura: Mínimo 70-80%, código crítico 100%
- Ejecución rápida: Unit tests deben ejecutarse en milisegundos
Integration Testing
El (como se discute en Dynamic Testing: Testing in Action) integration testing verifica interacciones entre componentes o sistemas integrados. Detecta defectos de interfaz que los unit tests omiten.
Objetivos
- Verificar interfaces entre componentes
- Probar flujo de datos entre módulos
- Validar contratos de API
- Detectar bugs de integración temprano
- Probar escenarios de interacción de componentes
Base de Pruebas
- Documentos de diseño de software y sistema
- Diagramas de arquitectura
- Especificaciones de API
- Definiciones de interfaz
- Descripciones de casos de uso
Enfoques de Integración
Integración Big Bang:
Todos los componentes integrados simultáneamente → Probar todo de una vez
Pros: Rápido de configurar
Contras: Difícil aislar defectos, arriesgado
Integración Incremental:
Componentes integrados y probados incrementalmente
Top-Down: Comenzar con módulos de alto nivel, stub niveles inferiores
Bottom-Up: Comenzar con módulos de bajo nivel, crear drivers para niveles superiores
Sandwich: Combinación de top-down y bottom-up
Ejemplo: API Integration Testing
// Ejemplo: Testing de integración entre autenticación y servicio de usuario
const request = require('supertest');
const app = require('../app');
describe('Integración de Autenticación y Servicio de Usuario', () => {
let authToken;
let userId;
beforeAll(async () => {
// Configurar base de datos de prueba
await setupTestDatabase() (como se discute en [Grey Box Testing: Best of Both Worlds](/blog/grey-box-testing));
});
afterAll(async () => {
// Limpiar base de datos de prueba
await cleanupTestDatabase();
});
test('Registro de usuario crea usuario y retorna auth token', async () => {
const newUser = {
email: 'integration@test.com',
password: 'SecurePass123!',
name: 'Integration Test User'
};
const response = await request(app)
.post('/api/auth/register')
.send(newUser)
.expect(201);
// Verificar estructura de respuesta
expect(response.body).toHaveProperty('token');
expect(response.body).toHaveProperty('user');
expect(response.body.user.email).toBe(newUser.email);
// Guardar para tests subsecuentes
authToken = response.body.token;
userId = response.body.user.id;
});
test('Usuario autenticado puede acceder endpoint de perfil', async () => {
const response = await request(app)
.get(`/api/users/${userId}/profile`)
.set('Authorization', `Bearer ${authToken}`)
.expect(200);
expect(response.body.email).toBe('integration@test.com');
expect(response.body.name).toBe('Integration Test User');
});
test('Solicitud no autenticada a perfil retorna 401', async () => {
await request(app)
.get(`/api/users/${userId}/profile`)
.expect(401);
});
test('Token inválido retorna 403', async () => {
await request(app)
.get(`/api/users/${userId}/profile`)
.set('Authorization', 'Bearer invalid_token_here')
.expect(403);
});
test('Usuario puede actualizar perfil con auth token válido', async () => {
const updates = {
name: 'Updated Name',
bio: 'Integration testing is awesome'
};
const response = await request(app)
.patch(`/api/users/${userId}/profile`)
.set('Authorization', `Bearer ${authToken}`)
.send(updates)
.expect(200);
expect(response.body.name).toBe('Updated Name');
expect(response.body.bio).toBe('Integration testing is awesome');
});
test('Logout invalida auth token', async () => {
// Logout
await request(app)
.post('/api/auth/logout')
.set('Authorization', `Bearer ${authToken}`)
.expect(200);
// Intentar acceder a perfil con token invalidado
await request(app)
.get(`/api/users/${userId}/profile`)
.set('Authorization', `Bearer ${authToken}`)
.expect(401);
});
});
Defectos Típicos Encontrados
- Parámetros incorrectos de llamadas API
- Desajustes de formato de datos entre componentes
- Manejo de errores faltante o incorrecto entre servicios
- Problemas de timing (condiciones de carrera, deadlocks)
- Suposiciones incorrectas sobre comportamiento de componentes
- Problemas de conexión a base de datos
- Fallos de comunicación de cola de mensajes
Mejores Prácticas
- Probar escenarios realistas: Usar flujos de integración reales
- Usar bases de datos de prueba: Aislar de datos de producción
- Probar condiciones de error: Fallos de red, timeouts, respuestas inválidas
- Mantener datos de prueba: Crear fixtures reutilizables
- Ejecutar en CI/CD: Automatizar ejecución en cada build
- Contract testing: Verificar contratos API entre servicios
System Testing
El system testing valida el sistema completo e integrado contra requisitos especificados. Prueba escenarios end-to-end desde perspectiva de usuario.
Objetivos
- Verificar que sistema cumple requisitos funcionales
- Validar comportamiento del sistema en entornos realistas
- Probar requisitos no funcionales (performance, seguridad, usabilidad)
- Verificar integración apropiada del sistema con sistemas externos
- Validar flujos de trabajo completos de usuario
Base de Pruebas
- Especificaciones de requisitos de sistema y software
- Casos de uso y user stories
- Documentos de arquitectura del sistema
- Descripciones de procesos de negocio
- Documentación de usuario
¿Quién Realiza System Testing?
Equipo de testing independiente o ingenieros de QA, separados del equipo de desarrollo para asegurar objetividad.
Tipos de System Testing
Tipo | Enfoque | Ejemplo |
---|---|---|
Funcional | Funcionalidades trabajan según especificado | Proceso de checkout e-commerce |
Performance | Tiempos de respuesta, throughput | Cargar 1000 usuarios concurrentes |
Seguridad | Vulnerabilidades, control de acceso | Ataques SQL injection, XSS |
Usabilidad | Experiencia de usuario, facilidad de uso | Navegación, validación de formularios |
Compatibilidad | Funciona en diferentes entornos | Cross-browser, dispositivos móviles |
Recuperación | Sistema se recupera de fallos | Recuperación de caída de BD |
Instalación | Deployment y configuración | Instalación limpia, escenarios de upgrade |
Ejemplo: Escenarios de System Test
# System Test Funcional: Flujo de Compra E-commerce
Feature: Flujo completo de compra
Como cliente
Quiero comprar productos
Para que pueda recibirlos en mi dirección
Background:
Given el sistema e-commerce está funcionando
And existen productos de prueba en inventario
And existe cuenta de usuario de prueba "systemtest@example.com"
Scenario: Compra exitosa de producto con tarjeta de crédito
Given estoy autenticado como "systemtest@example.com"
When busco "wireless headphones"
And selecciono "Sony WH-1000XM4" de resultados
And hago clic en "Agregar al Carrito"
And procedo al checkout
And ingreso dirección de envío:
| Campo | Valor |
| Calle | 123 Test St |
| Ciudad | San Francisco |
| Estado | CA |
| Código | 94105 |
And selecciono "Tarjeta de Crédito" como método de pago
And ingreso detalles válidos de tarjeta de crédito
And confirmo la orden
Then debo ver mensaje "Orden Confirmada"
And debo recibir email de confirmación de orden en menos de 2 minutos
And el estado de orden debe ser "Procesando" en mi cuenta
And el inventario debe decrementarse por 1 para "Sony WH-1000XM4"
And el pago debe procesarse por $349.99
Scenario: Manejo de producto sin stock
Given producto "Limited Edition Watch" tiene 0 inventario
When intento agregarlo al carrito
Then debo ver notificación "Fuera de Stock"
And botón "Agregar al Carrito" debe estar deshabilitado
And debo ver opción "Notificarme cuando esté disponible"
Scenario: Información de pago inválida
Given tengo artículos en mi carrito
When procedo al checkout
And ingreso número de tarjeta de crédito inválido "1234 5678 9012 3456"
And confirmo la orden
Then debo ver error "Información de pago inválida"
And orden no debe crearse
And inventario no debe decrementarse
Ejemplo de Performance Testing
# Ejemplo: Load testing para validación de performance del sistema
from locust import HttpUser, task, between
class EcommerceUser(HttpUser):
wait_time = between(1, 3) # Esperar 1-3 segundos entre requests
def on_start(self):
"""Login cuando usuario inicia"""
response = self.client.post("/api/auth/login", json={
"email": "loadtest@example.com",
"password": "TestPass123!"
})
self.token = response.json()["token"]
@task(3)
def browse_products(self):
"""Navegar productos (alta frecuencia)"""
self.client.get("/api/products", headers={
"Authorization": f"Bearer {self.token}"
})
@task(2)
def view_product_detail(self):
"""Ver producto específico (frecuencia media)"""
self.client.get("/api/products/12345", headers={
"Authorization": f"Bearer {self.token}"
})
@task(1)
def add_to_cart(self):
"""Agregar producto al carrito (menor frecuencia)"""
self.client.post("/api/cart/items", json={
"productId": "12345",
"quantity": 1
}, headers={
"Authorization": f"Bearer {self.token}"
})
@task(1)
def view_cart(self):
"""Ver carrito de compras"""
self.client.get("/api/cart", headers={
"Authorization": f"Bearer {self.token}"
})
# Ejecutar con: locust -f system_load_test.py --users 1000 --spawn-rate 50
# Prueba sistema con 1000 usuarios concurrentes, aumentando 50 usuarios por segundo
Defectos Típicos Encontrados
- Requisitos funcionales no cumplidos
- Errores de lógica de negocio
- Fallos de workflow end-to-end
- Cuellos de botella de performance
- Vulnerabilidades de seguridad
- Problemas de compatibilidad entre browsers/dispositivos
- Mensajes de error o manejo incorrecto
- Problemas de integridad de datos
Mejores Prácticas
- Probar en entorno similar a producción: Igualar arquitectura, volúmenes de datos
- Usar datos de prueba realistas: Representar patrones de uso real
- Automatizar tests de regresión: Funcionalidad core debe estar automatizada
- Probar requisitos no funcionales: Performance, seguridad, escalabilidad
- Documentar resultados de pruebas: Reporting comprensivo para stakeholders
- Priorizar por riesgo: Enfocarse primero en flujos críticos de negocio
User Acceptance Testing (UAT)
UAT valida que el sistema cumple requisitos de negocio y está listo para deployment. Usuarios reales prueban en escenarios realistas.
Objetivos
- Verificar que sistema cumple necesidades de negocio
- Validar usabilidad desde perspectiva de usuario final
- Asegurar que sistema está listo para deployment a producción
- Ganar confianza de stakeholders
- Identificar brechas entre requisitos e implementación
Base de Pruebas
- Requisitos y procesos de negocio
- Requisitos de usuario y user stories
- Manuales de usuario y materiales de training
- Flujos de trabajo de procesos de negocio
- Criterios de aceptación
¿Quién Realiza UAT?
Usuarios de negocio, product owners, stakeholders—las personas que realmente usarán el sistema.
Tipos de UAT
Alpha Testing:
- Realizado por personal interno (no equipo de desarrollo)
- Conducido en entorno de desarrollo
- Feedback temprano antes de usuarios externos
Beta Testing:
- Realizado por usuarios finales reales
- Conducido en producción o entorno cercano a producción
- Escenarios de uso del mundo real
- Feedback recolectado para mejoras finales
Contract Acceptance Testing:
- Valida que sistema cumple especificaciones de contrato
- A menudo legalmente vinculante
- Realizado antes de pago/entrega
Operational Acceptance Testing:
- Valida preparación del sistema para operación
- Prueba backup/recuperación, mantenibilidad, seguridad
- A menudo realizado por equipo de operaciones
Ejemplo: UAT Test Case
## UAT Test Case: Generación de Reporte Financiero Mensual
**Test ID:** UAT-FIN-001
**Feature:** Reportes Financieros
**Prioridad:** Alta
**Tester:** Jane Smith (Gerente de Finanzas)
**Fecha:** 2025-10-02
### Requisito de Negocio
Gerentes de finanzas necesitan generar reportes financieros mensuales comprensivos
que resuman ingresos, gastos y márgenes de ganancia por departamento.
### Pre-condiciones
- Usuario tiene rol de Gerente de Finanzas
- Existen datos financieros para Septiembre 2025
- Usuario está autenticado en el sistema
### Pasos de Prueba
1. Navegar a Reportes → Financiero → Resumen Mensual
- **Esperado:** Página de Resumen Mensual carga en menos de 3 segundos
- **Real:** _______________
- **Pasa/Falla:** ___________
2. Seleccionar "Septiembre 2025" del dropdown de mes
- **Esperado:** Mes seleccionado, formulario se actualiza
- **Real:** _______________
- **Pasa/Falla:** ___________
3. Seleccionar "Todos los Departamentos" o departamento específico
- **Esperado:** Selección de departamento disponible
- **Real:** _______________
- **Pasa/Falla:** ___________
4. Hacer clic en botón "Generar Reporte"
- **Esperado:** Reporte se genera en menos de 10 segundos, indicador de progreso se muestra
- **Real:** _______________
- **Pasa/Falla:** ___________
5. Revisar contenidos del reporte para exactitud
- **Esperado:** Reporte incluye:
- Ingresos totales por departamento
- Gastos totales por categoría
- Cálculos de margen de ganancia
- Comparación mes a mes
- Gráficos visuales (tendencia de ingresos, desglose de gastos)
- **Real:** _______________
- **Pasa/Falla:** ___________
6. Verificar que números del reporte coinciden con datos fuente
- **Esperado:** Verificar manualmente que 3 transacciones de muestra aparecen correctamente
- **Real:** _______________
- **Pasa/Falla:** ___________
7. Exportar reporte como PDF
- **Esperado:** PDF se descarga, mantiene formato, incluye todos los datos
- **Real:** _______________
- **Pasa/Falla:** ___________
8. Exportar reporte como Excel
- **Esperado:** Excel se descarga, datos son editables, fórmulas incluidas
- **Real:** _______________
- **Pasa/Falla:** ___________
### Criterios de Aceptación de Negocio
- [ ] Reporte refleja exactamente datos financieros
- [ ] Reporte se genera dentro de tiempo aceptable (< 10 segundos)
- [ ] Formato de reporte cumple estándares de negocio
- [ ] Funcionalidad de exportación funciona para PDF y Excel
- [ ] Reporte es comprensible sin conocimiento técnico
### Comentarios/Issues
_____________________________________________________________________
_____________________________________________________________________
### Resultado General: PASA / FALLA / PASA CONDICIONALMENTE
**Firmado:** _____________________ **Fecha:** __________
Defectos Típicos Encontrados
- Requisitos malinterpretados por equipo de desarrollo
- Problemas de usabilidad (UI confusa, mensajes poco claros)
- Reglas de negocio faltantes o casos extremos
- Cálculos o lógica de negocio incorrectos
- Reportes no coinciden con formato esperado
- Workflow no coincide con proceso de negocio real
- Problemas de performance con volúmenes de datos realistas
Mejores Prácticas
- Involucrar usuarios reales: No proxies o QA fingiendo ser usuarios
- Usar datos realistas: Enmascarar datos de producción si es necesario
- Documentar claramente: Usar lenguaje no técnico
- Programar tiempo adecuado: No apresurar fase UAT
- Definir criterios de aceptación claros: Pasa/falla binario por requisito
- Proporcionar capacitación: Ayudar a usuarios entender qué probar
- Recopilar feedback: Más allá de pasa/falla, recolectar sugerencias de mejora
Comparando Niveles de Testing
Aspecto | Unit | Integration | System | UAT |
---|---|---|---|---|
Realizado por | Desarrolladores | Desarrolladores/QA | Equipo QA | Usuarios de Negocio |
Enfoque | Componentes individuales | Interacciones de componentes | Sistema completo | Requisitos de negocio |
Entorno | Máquina de desarrollador | Entorno de prueba | Staging/Test | Similar a producción |
Base de Pruebas | Código, docs de diseño | Specs de interfaz | Requisitos | Necesidades de negocio |
Automatización | Altamente automatizado | Mayormente automatizado | Parcialmente automatizado | Mayormente manual |
Velocidad de Ejecución | Milisegundos | Segundos | Minutos | Horas/Días |
Cuándo | Durante codificación | Después de unit testing | Después de integration | Antes de release |
Defectos Típicos | Errores de lógica | Problemas de interfaz | Bugs end-to-end | Brechas de requisitos |
Estrategia de Niveles de Testing
Costo de Defectos por Nivel
Defecto encontrado en: | Costo de corrección
-----------------------|---------------------
Unit Testing | $ (1x)
Integration Testing | $$ (10x)
System Testing | $$$ (100x)
UAT | $$$$ (1000x)
Producción | $$$$$ (10,000x)
Principio Clave: Encontrar defectos lo más temprano posible.
Distribución de Cobertura de Pruebas
Seguir la pirámide de testing:
/\
/ \ UAT (Manual)
/ 10%\ - Journeys críticos de usuario
/------\ - Validación de negocio
/ \
/ 30% \ System Tests (Mayormente Automatizados)
/ Tests \ - Escenarios end-to-end
/--------------\- Testing no funcional
/ \
/ 60% Unit \ Unit & Integration Tests (Completamente Automatizados)
/ & Integration \- Rápidos, confiables, cobertura extensiva
/____Tests_________\
Conclusión
Comprender los niveles de testing permite a los equipos:
- Estructurar actividades de testing lógicamente a través del ciclo de vida de desarrollo
- Asignar responsabilidades apropiadamente entre desarrolladores, QA y usuarios de negocio
- Optimizar detección de defectos detectando issues en la etapa más temprana y económica
- Balancear testing manual y automatizado a través de diferentes niveles
- Asegurar cobertura comprensiva desde componentes individuales hasta flujos de trabajo de negocio completos
Cada nivel de testing sirve un propósito distinto. El éxito proviene de ejecutar todos los niveles apropiadamente, con cobertura de pruebas adecuada, responsabilidades claras y énfasis en detección temprana de defectos a través de unit e integration testing mientras se valida valor de negocio a través de system testing y UAT. Para más información sobre testing funcional en estos niveles, consulta nuestra guía completa de pruebas funcionales.