El Model-Based Testing (MBT) representa un cambio de paradigma en la automatización de pruebas, donde las pruebas se generan automáticamente desde modelos abstractos del comportamiento del sistema en lugar de escribirse manualmente. Este enfoque permite a los testers enfocarse en modelar el comportamiento esperado mientras las herramientas se encargan del trabajo mecánico de generación y ejecución de casos de prueba.
¿Qué es Model-Based Testing?
Model-Based Testing utiliza modelos formales o semiformales para representar el comportamiento esperado de un sistema bajo prueba. Estos modelos—como máquinas de estado, diagramas UML o tablas de decisión—sirven como fuente única de verdad desde la cual se derivan automáticamente casos de prueba, datos de prueba y resultados esperados.
Conceptos Clave
Modelo: Representación abstracta del comportamiento del sistema que captura estados, transiciones, entradas y salidas esperadas.
Algoritmo de Generación de Pruebas: Lógica que recorre el modelo para crear caminos de prueba que cumplan criterios específicos de cobertura.
Criterios de Cobertura: Reglas que definen qué tan exhaustivamente debe explorarse el modelo (ej. todos los estados, todas las transiciones, todos los caminos).
Oráculo: Mecanismo para determinar resultados esperados, derivado directamente de la especificación del modelo.
Tipos de Modelos en MBT
Modelos de Máquinas de Estado
Las máquinas de estado son excelentes para modelar sistemas con modos operacionales distintos y transiciones de estado.
# Ejemplo: Modelo de máquina de estado para sistema de login
from graphwalker import Model, Edge, Vertex
class LoginModel(Model):
def __init__(self):
super().__init__()
# Definir vértices (estados)
logged_out = Vertex("LoggedOut")
login_page = Vertex("LoginPage")
logged_in = Vertex("LoggedIn")
locked_out = Vertex("LockedOut")
# Definir aristas (transiciones)
self.add_edge(Edge(logged_out, login_page, "navigateToLogin"))
self.add_edge(Edge(login_page, logged_in, "loginSuccess"))
self.add_edge(Edge(login_page, login_page, "loginFailure"))
self.add_edge(Edge(login_page, locked_out, "threeFailedAttempts"))
self.add_edge(Edge(logged_in, logged_out, "logout"))
self.start_vertex = logged_out
Diagramas de Actividad UML
Los diagramas de actividad modelan flujos de trabajo y procesos de negocio, ideales para procedimientos complejos de múltiples pasos.
Ventajas:
- Representación visual del flujo del proceso
- Captura puntos de decisión y actividades paralelas
- Notación familiar para stakeholders
- Soporta swimlanes para escenarios multi-actor
Tablas de Decisión
Las tablas de decisión representan de forma compacta lógica condicional compleja con múltiples combinaciones de entrada.
Intentos Login | Password Válido | Estado Cuenta | Resultado |
---|---|---|---|
≤ 3 | Sí | Activa | Éxito |
≤ 3 | No | Activa | Fallo |
> 3 | Cualquiera | Activa | Bloqueo |
Cualquiera | Cualquiera | Bloqueada | Error Bloqueado |
Cualquiera | Sí | Inactiva | Error Inactivo |
Herramientas y Frameworks MBT
GraphWalker (Java/Python)
GraphWalker genera secuencias de prueba caminando a través de grafos dirigidos que representan el comportamiento del sistema.
// Definición de modelo GraphWalker
public class ShoppingCartModel implements Model {
@Action
public void e_AddItem() {
// Acción: Agregar ítem al carrito
cart.addItem(testProduct);
}
@Action
public void e_RemoveItem() {
// Acción: Remover ítem del carrito
cart.removeItem(testProduct);
}
@Action
public void e_Checkout() {
// Acción: Proceder al checkout
cart.checkout();
}
@State
public void v_EmptyCart() {
// Verificar: Carrito vacío
assertTrue(cart.isEmpty());
}
@State
public void v_CartWithItems() {
// Verificar: Carrito contiene ítems
assertFalse(cart.isEmpty());
}
@State
public void v_CheckoutPage() {
// Verificar: En página de checkout
assertTrue(page.isCheckoutPage());
}
}
ModelJUnit (Java)
ModelJUnit usa código Java para especificar modelos y genera pruebas usando algoritmos aleatorios o greedy.
public class ElevatorModel implements FsmModel {
private int floor = 0;
private int target = 0;
private boolean doorsOpen = false;
public boolean selectFloorGuard() {
return !doorsOpen;
}
@Action
public void selectFloor() {
target = random.nextInt(10);
}
@Action
public void openDoors() {
doorsOpen = true;
}
@Action
public void closeDoors() {
doorsOpen = false;
}
public boolean moveUpGuard() {
return floor < target && !doorsOpen;
}
@Action
public void moveUp() {
floor++;
}
public boolean moveDownGuard() {
return floor > target && !doorsOpen;
}
@Action
public void moveDown() {
floor--;
}
}
Spec Explorer (Microsoft)
Spec Explorer se especializa en testing de protocolos y validación de APIs usando Cord (C# para modelado).
Características Clave:
- Programas de modelo escritos en C#
- Estrategias de exploración para espacio de estados
- Slicing y filtrado de casos de prueba
- Integración con Visual Studio
Criterios de Cobertura
Diferentes criterios de cobertura balancean exhaustividad contra tamaño de suite de pruebas.
Cobertura de Estados
Visitar cada estado en el modelo al menos una vez.
Ventaja: Asegura que todos los modos del sistema sean probados Limitación: Puede omitir combinaciones críticas de transiciones
Cobertura de Transiciones
Ejercitar cada transición en el modelo al menos una vez.
Ventaja: Más exhaustiva que cobertura de estados Tamaño de Suite: Moderado
Cobertura de Caminos
Ejecutar todos los caminos posibles a través del modelo.
Ventaja: Testing comprehensivo Limitación: A menudo inviable debido a explosión combinatoria
Criterios Prácticos Comunes
Criterio | Objetivo de Cobertura | Tamaño de Suite | Caso de Uso |
---|---|---|---|
Cobertura Estados | Todos los estados | Pequeño | Smoke testing |
Cobertura Transiciones | Todas las transiciones | Mediano | Regresión estándar |
Transiciones 2-Way | Todos los pares transición | Grande | Testing exhaustivo |
Cobertura Edge-Pair | Todos los pares arista | Grande | Sistemas críticos |
Todos los Caminos | Todos los caminos posibles | Exponencial | Solo modelos pequeños |
Flujo de Trabajo MBT
Paso 1: Creación del Modelo
Analizar requisitos y crear un modelo abstracto que capture el comportamiento del sistema.
# Ejemplo: Modelo ATM en Python
class ATMModel:
def __init__(self):
self.state = "idle"
self.balance = 1000
self.pin_attempts = 0
def insert_card(self):
if self.state == "idle":
self.state = "card_inserted"
return True
return False
def enter_pin(self, correct):
if self.state == "card_inserted":
if correct:
self.state = "authenticated"
self.pin_attempts = 0
else:
self.pin_attempts += 1
if self.pin_attempts >= 3:
self.state = "card_captured"
return True
return False
def withdraw(self, amount):
if self.state == "authenticated":
if amount <= self.balance:
self.balance -= amount
return True
return False
Paso 2: Generación de Pruebas
Configurar criterios de cobertura y generar secuencias de prueba automáticamente.
# Ejemplo CLI GraphWalker
graphwalker offline \
--model shopping-cart.json \
--generator "random(edge_coverage(100))" \
--output tests.json
Paso 3: Ejecución de Pruebas
Ejecutar pruebas generadas contra el sistema real, comparando resultados con predicciones del modelo.
// Ejecutar secuencia de prueba generada
GraphWalker walker = new GraphWalker(new ShoppingCartModel());
walker.setPathGenerator(new RandomPath(new EdgeCoverage(100)));
while (walker.hasNextStep()) {
walker.getNextStep().execute();
assertTrue(walker.getCurrentVertex().verify());
}
Paso 4: Análisis y Refinamiento
Analizar fallos para determinar si el defecto está en el sistema o en el modelo.
Beneficios del Model-Based Testing
Mayor Cobertura de Pruebas
La generación automatizada explora caminos que testers humanos podrían pasar por alto.
Caso de Estudio: Compañía telecom usando MBT incrementó cobertura de transiciones de 67% a 98%, descubriendo 12 defectos previamente desconocidos.
Mantenimiento Reducido
Actualiza el modelo una vez; las pruebas se regeneran automáticamente.
Ejemplo ROI: 60% de reducción en esfuerzo de mantenimiento de pruebas cuando los requisitos cambian frecuentemente.
Detección Temprana de Defectos
La creación del modelo fuerza especificación precisa, revelando ambigüedades en requisitos.
Impacto Shift-Left: 40% de defectos encontrados durante fase de modelado, antes de que exista código.
Documentación Viva
Los modelos sirven como especificaciones ejecutables que nunca quedan obsoletas.
Desafíos y Limitaciones
Esfuerzo de Creación del Modelo
Construir modelos precisos requiere inversión inicial significativa.
Mitigación: Comenzar con flujos críticos; expandir cobertura iterativamente.
Curva de Aprendizaje de Herramientas
Las herramientas MBT tienen curvas de aprendizaje más pronunciadas que frameworks basados en scripts.
Mitigación: Invertir en entrenamiento; construir expertise interna gradualmente.
Sistemas No Determinísticos
Sistemas con comportamiento impredecible (ej. dependencias externas pesadas) son más difíciles de modelar.
Mitigación: Modelar en niveles de abstracción más altos; usar stubs para sistemas externos.
Validez del Modelo
Los modelos pueden divergir del comportamiento real del sistema con el tiempo.
Mitigación: Validación continua; checks automatizados de trazabilidad modelo-a-código.
Mejores Prácticas
Comenzar Pequeño
Empezar con un modelo limitado que cubra funcionalidad core.
# Modelo inicial: Solo autenticación core de usuario
class MinimalAuthModel:
states = ["logged_out", "logged_in"]
transitions = [
("logged_out", "login_success", "logged_in"),
("logged_in", "logout", "logged_out")
]
Colaborar con Stakeholders
Involucrar expertos de dominio en revisión del modelo para asegurar precisión.
Técnica: Desarrollo de modelo basado en workshops con analistas de negocio y desarrolladores.
Combinar con Testing Tradicional
Usar MBT para lógica compleja; usar pruebas scriptadas para casos simples.
Estrategia Híbrida: 70% MBT para features dependientes de estado, 30% scriptado para validación UI.
Mantener Trazabilidad del Modelo
Vincular elementos del modelo a requisitos para análisis de impacto.
# Mapeo de trazabilidad
transitions:
- id: T1
name: "login_success"
requirements: [REQ-AUTH-001, REQ-AUTH-003]
- id: T2
name: "login_failure"
requirements: [REQ-AUTH-002]
Control de Versiones de Modelos
Tratar modelos como artefactos de primera clase en control de versiones.
# Estructura Git para MBT
models/
├── authentication.graphml
├── shopping-cart.json
└── payment-flow.yaml
tests/
├── generated/
└── manual/
Aplicaciones del Mundo Real
Testing de Sistemas Embebidos
Fabricante automotriz usa MBT para testing de ECU, generando miles de casos de prueba desde máquinas de estado.
Resultados: 35% reducción en defectos escapados, 50% más rápido testing de regresión.
Testing de Aplicaciones Web
Plataforma e-commerce modela flujos de checkout con 15 estados y 40 transiciones.
Resultados: Descubrió 8 casos edge no cubiertos por pruebas manuales.
Testing de API
Firma de servicios financieros modela transiciones de estado de API REST para procesamiento de pagos.
Resultados: 100% cobertura de transiciones alcanzada; modelo sirve como documentación de API.
Conclusión
Model-Based Testing cambia el foco del testing de casos de prueba individuales a modelos comprehensivos de comportamiento. Si bien requiere inversión inicial significativa en habilidades de modelado y herramientas, MBT entrega beneficios sustanciales a largo plazo en cobertura, mantenibilidad y detección de defectos—especialmente para sistemas complejos con comportamiento rico dependiente de estado.
El éxito con MBT requiere tratar los modelos como artefactos vivos, manteniendo su precisión mediante refinamiento y validación continua contra el comportamiento del sistema en evolución.