Las pruebas de caja negra son un enfoque fundamental donde el tester examina la funcionalidad de una aplicación sin conocer su estructura de código interna, detalles de implementación o rutas internas. El enfoque se centra completamente en las entradas y salidas esperadas, tratando el software como una “caja negra”.
¿Qué son las Pruebas de Caja Negra?
Las pruebas de caja negra, también conocidas como pruebas de comportamiento o basadas en especificaciones, validan la funcionalidad del software contra requisitos y especificaciones. Los testers no necesitan conocimientos de programación ni acceso al código fuente—solo necesitan entender qué debería hacer el sistema.
Características Clave
- Basadas en especificaciones: Las pruebas se derivan de requisitos, historias de usuario o especificaciones
- Sin acceso al código: Los testers no examinan la estructura del código interno
- Perspectiva del usuario final: Las pruebas simulan el comportamiento y escenarios reales del usuario
- Enfoque en funcionalidad: Valida si las características funcionan como se pretende
- Independiente de implementación: Las pruebas permanecen válidas incluso si cambia la implementación interna
Cuándo Usar Pruebas de Caja Negra
Las pruebas de caja negra son ideales para:
- Pruebas funcionales: Validar características contra requisitos
- Pruebas de sistema: Probar el sistema completo integrado
- Pruebas de aceptación: Verificar que el software cumple las necesidades del negocio
- Pruebas de regresión: Asegurar que los cambios no rompan la funcionalidad existente
- Pruebas Beta/UAT: Usuarios reales probando en entornos similares a producción
Técnicas Principales de Pruebas de Caja Negra
1. Partición de Equivalencia
La partición de equivalencia divide los datos de entrada en particiones válidas e inválidas donde todas las condiciones en una partición se comportan de manera similar. En lugar de probar todas las entradas posibles, pruebas un valor representativo de cada partición.
Ejemplo: Validación de edad para solicitud de seguro
# Entrada: La edad debe estar entre 18-65 para elegibilidad
# Clases de equivalencia:
# Inválida: edad < 18
# Válida: 18 ≤ edad ≤ 65
# Inválida: edad > 65
# Casos de prueba (uno de cada partición):
test_cases = [
(15, False), # Debajo del mínimo
(30, True), # Rango válido
(70, False) # Arriba del máximo
]
def test_age_eligibility():
for age, expected in test_cases:
result = check_eligibility(age)
assert result == expected, f"Falló para edad {age}"
2. Análisis de Valores Límite (AVL)
El análisis de valores límite se enfoca en probar en los bordes de las particiones de equivalencia, donde los defectos ocurren con frecuencia. Prueba valores en los límites, justo debajo y justo arriba.
Ejemplo: Validación de longitud de contraseña
# Requisito: La contraseña debe tener 8-20 caracteres
# Valores de prueba:
boundary_tests = [
("1234567", False), # Longitud 7 (justo debajo del mínimo)
("12345678", True), # Longitud 8 (límite mínimo)
("123456789", True), # Longitud 9 (justo arriba del mínimo)
("12345678901234567890", True), # Longitud 20 (límite máximo)
("123456789012345678901", False) # Longitud 21 (justo arriba del máximo)
]
def test_password_length():
for password, expected in boundary_tests:
result = validate_password(password)
assert result == expected, f"Falló para longitud {len(password)}"
3. Pruebas con Tabla de Decisión
Las tablas de decisión mapean combinaciones de entradas a acciones o salidas esperadas. Son perfectas para probar lógica de negocio compleja con múltiples condiciones.
Ejemplo: Sistema de aprobación de préstamos
Puntaje Crédito | Ingreso | Empleo | Préstamo Aprobado |
---|---|---|---|
Alto (>700) | Alto | Estable | ✓ Sí |
Alto | Bajo | Estable | ✓ Sí |
Alto | Alto | Inestable | ✓ Sí |
Alto | Bajo | Inestable | ✗ No |
Bajo (<700) | Alto | Estable | ✗ No |
Bajo | Bajo | Estable | ✗ No |
Bajo | Alto | Inestable | ✗ No |
Bajo | Bajo | Inestable | ✗ No |
def test_loan_approval():
test_scenarios = [
# (puntaje_credito, ingreso, empleo, esperado)
(750, "alto", "estable", True),
(750, "bajo", "estable", True),
(750, "alto", "inestable", True),
(750, "bajo", "inestable", False),
(650, "alto", "estable", False),
(650, "bajo", "estable", False),
(650, "alto", "inestable", False),
(650, "bajo", "inestable", False),
]
for score, ingreso, empleo, esperado in test_scenarios:
result = approve_loan(score, ingreso, empleo)
assert result == esperado
4. Pruebas de Transición de Estado
Las pruebas de transición de estado validan cómo se comporta un sistema al transitar entre diferentes estados basándose en eventos o entradas.
Ejemplo: Sistema de procesamiento de pedidos
Estados: Nuevo → Pagado → Enviado → Entregado → Completado
↓ ↓ ↓ ↓
Cancelado (desde cualquier estado)
class OrderState:
NEW = "nuevo"
PAID = "pagado"
SHIPPED = "enviado"
DELIVERED = "entregado"
COMPLETED = "completado"
CANCELLED = "cancelado"
def test_order_state_transitions():
# Transiciones válidas
assert process_payment("nuevo") == "pagado"
assert ship_order("pagado") == "enviado"
assert deliver_order("enviado") == "entregado"
assert complete_order("entregado") == "completado"
# Transiciones inválidas
with pytest.raises(InvalidTransition):
ship_order("nuevo") # No se puede enviar pedido sin pagar
# Cancelación desde cualquier estado
for state in [OrderState.NEW, OrderState.PAID, OrderState.SHIPPED]:
assert cancel_order(state) == OrderState.CANCELLED
5. Pruebas de Casos de Uso
Las pruebas de casos de uso derivan casos de prueba de casos de uso que describen interacciones usuario-sistema. Cada caso de uso incluye flujo principal, flujos alternos y flujos de excepción.
Ejemplo: Caso de uso de inicio de sesión
Característica: Inicio de sesión de usuario
Escenario: Inicio de sesión exitoso con credenciales válidas
Dado que el usuario está en la página de inicio de sesión
Cuando el usuario ingresa usuario válido "juan@ejemplo.com"
Y el usuario ingresa contraseña válida "ContraseñaSegura123"
Y el usuario hace clic en el botón de inicio de sesión
Entonces el usuario debe ser redirigido al panel de control
Y debe mostrarse el mensaje de bienvenida "Bienvenido, Juan"
Escenario: Inicio de sesión fallido con contraseña inválida
Dado que el usuario está en la página de inicio de sesión
Cuando el usuario ingresa usuario válido "juan@ejemplo.com"
Y el usuario ingresa contraseña inválida "ContraseñaIncorrecta"
Y el usuario hace clic en el botón de inicio de sesión
Entonces debe mostrarse el mensaje de error "Credenciales inválidas"
Y el usuario debe permanecer en la página de inicio de sesión
Escenario: Bloqueo de cuenta después de intentos fallidos
Dado que el usuario ha fallado el inicio de sesión 2 veces
Cuando el usuario ingresa credenciales inválidas nuevamente
Entonces la cuenta debe bloquearse
Y debe mostrarse el mensaje de error "Cuenta bloqueada"
6. Pruebas All-Pairs (Por Pares)
Las pruebas all-pairs reducen el número de combinaciones de prueba mientras mantienen la cobertura. Asegura que cada par de parámetros de entrada se pruebe junto al menos una vez.
Ejemplo: Pruebas de compatibilidad de navegadores
# Parámetros:
# Navegador: Chrome, Firefox, Safari
# SO: Windows, macOS, Linux
# Resolución: 1920x1080, 1366x768
# Combinación completa: 3 × 3 × 2 = 18 pruebas
# Reducción all-pairs: ~9 pruebas
pairwise_combinations = [
("Chrome", "Windows", "1920x1080"),
("Chrome", "macOS", "1366x768"),
("Chrome", "Linux", "1920x1080"),
("Firefox", "Windows", "1366x768"),
("Firefox", "macOS", "1920x1080"),
("Firefox", "Linux", "1366x768"),
("Safari", "Windows", "1920x1080"),
("Safari", "macOS", "1366x768"),
("Safari", "Linux", "1920x1080"),
]
def test_browser_compatibility():
for navegador, so, resolucion in pairwise_combinations:
launch_browser(navegador, so, resolucion)
assert page_loads_correctly()
assert elements_render_properly()
Técnicas Avanzadas de Caja Negra
Adivinación de Errores
La adivinación de errores se basa en la experiencia del tester para anticipar dónde podrían ocurrir defectos. Los testers usan intuición y experiencias pasadas para identificar áreas problemáticas.
Escenarios comunes propensos a errores:
# Entradas típicas propensas a errores para probar
error_prone_tests = [
# Caracteres especiales
test_input("'; DROP TABLE users--"), # Inyección SQL
test_input("<script>alert('XSS')</script>"), # XSS
# Casos límite
test_input(""), # Entrada vacía
test_input(" " * 1000), # Entrada muy larga
test_input("null"), # Valores nulos
# Problemas de formato
test_input("0000-00-00"), # Fecha inválida
test_input("999-999-9999"), # Teléfono inválido
test_input("noesunemail"), # Email inválido
]
Pruebas Exploratorias
Las pruebas exploratorias son aprendizaje, diseño de pruebas y ejecución simultáneos. Los testers exploran la aplicación libremente, usando creatividad e intuición para encontrar defectos.
Sesión acotada en tiempo:
SESIÓN: Carrito de Compras - 60 minutos
MISIÓN: Explorar funcionalidad del carrito enfocándose en casos extremos
ÁREAS:
- Agregar/eliminar artículos
- Actualizaciones de cantidad
- Cálculos de precios
- Códigos promocionales
- Persistencia de sesión
HALLAZGOS:
1. El carrito no se actualiza al eliminar el último artículo
2. Se aceptan cantidades negativas vía API
3. Códigos promocionales vencidos se aplican exitosamente
4. El carrito se borra después del timeout de sesión sin advertencia
Mejores Prácticas de Pruebas de Caja Negra
1. Comenzar con Requisitos
Siempre basa las pruebas en requisitos documentados, historias de usuario o especificaciones. Requisitos claros llevan a casos de prueba efectivos.
Historia de Usuario: Como cliente, quiero buscar productos por nombre
Criterios de Aceptación:
- La búsqueda devuelve resultados que contienen el término buscado
- La búsqueda no distingue mayúsculas/minúsculas
- Los resultados se muestran en 2 segundos
- Máximo 20 resultados por página
- La búsqueda vacía devuelve todos los productos
Casos de Prueba:
- TC001: Búsqueda con nombre exacto del producto
- TC002: Búsqueda con nombre parcial
- TC003: Búsqueda con mayúsculas/minúsculas mezcladas
- TC004: Verificar tiempo de respuesta < 2s
- TC005: Verificar paginación a 20 artículos
- TC006: Verificar comportamiento de búsqueda vacía
2. Combinar Técnicas
Usa múltiples técnicas juntas para cobertura integral:
def test_registration_form():
# Partición de Equivalencia + AVL
test_email_formats()
test_password_strength()
# Tabla de Decisión
test_field_combinations()
# Transición de Estado
test_form_submission_states()
# Adivinación de Errores
test_special_characters()
test_sql_injection_attempts()
3. Priorizar Casos de Prueba
No todas las pruebas son igualmente importantes. Prioriza basándote en:
- Riesgo: Las características de alto riesgo necesitan más pruebas
- Uso: Las características usadas frecuentemente son críticas
- Complejidad: La lógica compleja necesita pruebas exhaustivas
- Impacto de negocio: Las características críticas para ingresos van primero
# Alta Prioridad
@pytest.mark.priority("alta")
def test_payment_processing():
"""Ruta crítica - impacto en ingresos"""
pass
# Prioridad Media
@pytest.mark.priority("media")
def test_search_functionality() (como se discute en [Bug Anatomy: From Discovery to Resolution](/blog/bug-anatomy)):
"""Usado frecuentemente - experiencia de usuario"""
pass
# Baja Prioridad
@pytest.mark.priority("baja")
def test_footer_links():
"""Bajo riesgo - impacto mínimo"""
pass
4. Documentar Casos de Prueba Claramente
def test_user_registration_with_valid_data():
"""
ID de Prueba: TC_REG_001
Descripción: Verificar que el usuario puede registrarse con datos válidos
Precondiciones:
- La aplicación es accesible
- El email no está ya registrado
Pasos de Prueba:
1. Navegar a la página de registro
2. Ingresar email válido
3. Ingresar contraseña válida (8+ caracteres)
4. Hacer clic en el botón Registrar
Resultado Esperado:
- El registro tiene éxito
- Usuario redirigido al panel de control
- Email de bienvenida enviado
Datos de Prueba:
- Email: nuevousuario@ejemplo.com
- Contraseña: ContraseñaVálida123
"""
# Implementación de la prueba
pass
Ejemplo Real de Pruebas de Caja Negra
Flujo de Pago de E-commerce
class TestCheckoutFlow:
"""Suite completa de pruebas de caja negra para checkout"""
def test_guest_checkout_valid_card(self):
"""Ruta feliz - usuario invitado, pago válido"""
# Agregar artículos al carrito
add_to_cart("Producto A", quantity=2)
add_to_cart("Producto B", quantity=1)
# Proceder al checkout
goto_checkout()
# Ingresar información de envío
fill_shipping({
"name": "Juan Pérez",
"address": "Calle Principal 123",
"city": "Madrid",
"zip": "28001"
})
# Ingresar pago
fill_payment({
"card_number": "4111111111111111",
"expiry": "12/25",
"cvv": "123"
})
# Enviar pedido
submit_order()
# Verificar
assert order_confirmed()
assert confirmation_email_sent()
assert inventory_updated()
def test_checkout_with_invalid_card(self):
"""Ruta de error - método de pago inválido"""
add_to_cart("Producto A")
goto_checkout()
fill_shipping(valid_shipping_data())
fill_payment({
"card_number": "4111111111111112", # Inválida
"expiry": "12/25",
"cvv": "123"
})
submit_order()
# Verificar manejo de errores
assert error_displayed("Pago rechazado")
assert order_not_created()
assert cart_preserved()
def test_checkout_with_expired_coupon(self):
"""Caso extremo - código promocional vencido"""
add_to_cart("Producto A")
goto_checkout()
apply_coupon("VENCIDO2024")
assert error_displayed("Cupón vencido")
assert discount_not_applied()
def test_checkout_performance(self):
"""No funcional - tiempo de respuesta"""
start = time.time()
complete_checkout_flow()
duration = time.time() - start
assert duration < 5.0, "El checkout tomó demasiado tiempo"
Ventajas y Limitaciones
Ventajas
- No se requiere conocimiento de programación: El equipo QA puede enfocarse en funcionalidad
- Perspectiva independiente: Los testers ven el software como los usuarios
- Cobertura de amplio alcance: Prueba todos los aspectos funcionales
- Pruebas reutilizables: Las pruebas sobreviven cambios de implementación
Limitaciones
- Cobertura de código limitada: Puede perder errores de lógica interna
- No puede probar algoritmos: Los cálculos internos no se verifican
- Brechas de cobertura de rutas: No se ejercitan todas las rutas de código
- Detección tardía de defectos: Los problemas se encuentran en etapas posteriores de prueba
Conclusión
Las pruebas de caja negra son esenciales para validar software desde la perspectiva del usuario. Al dominar técnicas de diseño de casos de prueba como partición de equivalencia, análisis de valores límite, tablas de decisión y pruebas de transición de estado, puedes crear suites de pruebas integrales que aseguren que el software cumpla los requisitos y entregue la funcionalidad esperada.
La clave para las pruebas de caja negra exitosas es combinar múltiples técnicas, priorizar efectivamente y mantener documentación clara. Ya sea que estés probando un formulario simple o un sistema empresarial complejo, las pruebas de caja negra proporcionan la base para el aseguramiento de la calidad.