Cuando una integración modifica datos, no alcanza con verificar que una operación responda correctamente. También debemos comprobar qué estado queda guardado y si ese estado es consistente.
Muchas funcionalidades realizan varias operaciones relacionadas: crear una orden, descontar stock, registrar un pago, actualizar un estado o publicar un evento. Si una parte falla y otra queda guardada, el sistema puede terminar con datos incoherentes.
En este tema estudiaremos transacciones, persistencia y consistencia de datos desde la perspectiva de las pruebas de integración.
La persistencia es la capacidad de guardar información para que permanezca disponible después de ejecutar una operación. En aplicaciones reales, suele estar asociada a bases de datos, archivos o almacenamiento externo.
En pruebas de integración, verificar persistencia significa comprobar que:
Una transacción agrupa varias operaciones para que se confirmen o se reviertan como una unidad. Si todo sale bien, se realiza un commit. Si algo falla, se realiza un rollback para deshacer los cambios.
Por ejemplo, al confirmar una compra puede ser necesario:
Si el descuento de stock falla, puede ser incorrecto dejar la orden creada como si todo hubiera terminado bien. La transacción ayuda a mantener la consistencia.
En bases de datos suele hablarse de propiedades ACID. Para un curso inicial, podemos resumirlas así:
| Propiedad | Idea principal | Pregunta para probar |
|---|---|---|
| Atomicidad | Todo se completa o todo se revierte. | ¿Quedan cambios parciales si algo falla? |
| Consistencia | Los datos quedan en un estado válido. | ¿Se respetan reglas y relaciones? |
| Aislamiento | Operaciones simultáneas no deberían interferirse incorrectamente. | ¿Dos operaciones concurrentes pisan datos? |
| Durabilidad | Lo confirmado permanece guardado. | ¿El dato sigue disponible después del commit? |
La consistencia significa que los datos guardados respetan las reglas del sistema. No alcanza con que existan registros; deben formar un estado válido.
Ejemplos de consistencia:
Las pruebas de integración deben observar estas relaciones cuando la operación afecta más de un dato.
Una prueba de integración debe verificar el estado final producido por la operación. Esto puede incluir consultas a la base, lectura de archivos, inspección de mensajes o revisión de estados en servicios simulados.
Al verificar el estado final, conviene preguntar:
Una prueba que solo mira la respuesta puede pasar aunque el sistema haya guardado datos incorrectos.
Probar el commit significa verificar que una operación exitosa confirme todos los cambios esperados.
Por ejemplo, al confirmar una compra con pago aprobado, la prueba podría verificar:
El objetivo es comprobar que el flujo exitoso deja un estado completo y usable por otros componentes.
Probar rollback significa verificar que una operación fallida no deje cambios parciales indebidos.
Supongamos que una compra falla al registrar el pago. Según la regla del sistema, puede esperarse que:
Lo importante es que el estado final sea el definido por el negocio, no un resultado accidental.
Algunas operaciones pasan por estados intermedios. Esto no siempre es incorrecto. Lo importante es que esos estados estén definidos y no queden abandonados por error.
Ejemplos:
Una prueba de integración debe distinguir entre un estado intermedio válido y un estado inconsistente causado por una falla.
Una operación es idempotente cuando repetirla produce el mismo resultado final sin duplicar efectos no deseados. Este concepto es muy importante en integraciones con reintentos.
Por ejemplo, si se reintenta la confirmación de un pago por un error de red, el sistema no debería crear dos órdenes ni descontar stock dos veces.
Una prueba de integración puede verificar que:
La concurrencia aparece cuando varias operaciones intentan modificar datos relacionados al mismo tiempo. En integración, esto puede generar problemas de consistencia.
Ejemplos:
No todas las suites de integración cubren concurrencia en profundidad, pero los casos críticos deben considerarla cuando el riesgo existe.
En sistemas con eventos o procesos asíncronos, no siempre todos los datos se actualizan al mismo tiempo. Puede existir consistencia eventual: el sistema tarda un tiempo en llegar al estado final esperado.
Por ejemplo, una orden puede crearse inmediatamente, pero el correo de confirmación o la actualización de un reporte pueden ocurrir segundos después.
Al probar estos casos, conviene:
En consistencia de datos, también es importante verificar efectos que no deben ocurrir. A veces una operación parece correcta porque produce una respuesta válida, pero genera efectos secundarios indebidos.
Ejemplos:
Estas verificaciones son esenciales para detectar errores de integración que dejan datos incorrectos sin generar una excepción visible.
Supongamos una prueba de integración para una compra cuyo pago es rechazado.
| Verificación | Resultado esperado |
|---|---|
| Respuesta al usuario | La operación informa que el pago fue rechazado. |
| Orden | No se crea, o queda en estado rechazado según la regla definida. |
| Stock | No se descuenta. |
| Pago | No queda registrado como aprobado. |
| Eventos | No se publica evento de compra confirmada. |
Esta prueba no se limita a verificar el error. También comprueba que el estado del sistema no quede contaminado por una operación fallida.
Al probar transacciones y consistencia, suelen aparecer errores como:
Para evaluar una prueba de integración relacionada con persistencia, podemos preguntar:
Las pruebas de integración deben demostrar que los cambios de datos ocurren de manera correcta, completa y consistente. Esto incluye caminos exitosos, fallas intermedias, reintentos y estados finales.
Cuando una operación modifica varias partes del sistema, una prueba útil no solo pregunta si la operación respondió bien. Pregunta qué quedó guardado, qué no debía cambiar y si el sistema puede continuar funcionando desde ese estado.
En el próximo tema veremos la integración entre capas de una aplicación, como controladores, servicios y repositorios.