En mi proyecto actual, estoy teniendo dificultades para encontrar una buena solución para crear pruebas de integración escalables que no tengan efectos secundarios. Una pequeña aclaración sobre la propiedad libre de efectos secundarios: se trata principalmente de la base de datos; no debería haber ningún cambio en la base de datos después de que se completen las pruebas (el estado debe conservarse). Tal vez la escalabilidad y la preservación del estado no se unen, pero realmente quiero impulsar una mejor solución.
Aquí hay una prueba de integración típica (estas pruebas tocan la capa de la base de datos):
public class OrderTests {
List<Order> ordersToDelete = new ArrayList<Order>();
public testOrderCreation() {
Order order = new Order();
assertTrue(order.save());
orderToDelete.add(order);
}
public testOrderComparison() {
Order order = new Order();
Order order2 = new Order();
assertFalse(order.isEqual(order2);
orderToDelete.add(order);
orderToDelete.add(order2);
}
// More tests
public teardown() {
for(Order order : ordersToDelete)
order.delete();
}
}
Como uno podría imaginar, este enfoque produce pruebas que son extremadamente lentas. Y, cuando se aplica a todas las pruebas de integración, toma alrededor de 5 segundos probar solo una pequeña porción del sistema. Me imagino que este número aumentará cuando aumente la cobertura.
¿Cuál sería otro enfoque para escribir tales pruebas?Una alternativa que se me ocurre es tener un tipo de variables globales (dentro de una clase) y todos los métodos de prueba comparten esta variable. Como resultado, solo unos pocos pedidos se crean y eliminan; resultando en pruebas más rápidas. Sin embargo, creo que esto presenta un problema mayor; las pruebas ya no están aisladas y se hace cada vez más difícil de entender y analizar.
Puede ser que las pruebas de integración no estén destinadas a ejecutarse con tanta frecuencia como las pruebas unitarias; por lo tanto, el bajo rendimiento podría ser aceptable para aquellos. En cualquier caso, sería genial saber si a alguien se le ocurrieron alternativas para mejorar la escalabilidad.
Este es el problema eterno que todos enfrentan al escribir pruebas de integración.
La solución ideal, particularmente si está probando en producción, es abrir una transacción en la configuración y revertirla en el desmontaje. Creo que esto debería adaptarse a tus necesidades.
Donde eso no sea posible, por ejemplo, cuando esté probando la aplicación desde la capa del cliente, otra solución es usar una base de datos en una máquina virtual y tomar una instantánea en la configuración y volver a ella en desmontaje (no tómese el tiempo que pueda esperar).
fuente
Las pruebas de integración siempre deben ejecutarse contra la configuración de producción. En su caso, significa que debe tener la misma base de datos y servidor de aplicaciones. Por supuesto, en aras del rendimiento, puede optar por utilizar un DB en memoria.
Sin embargo, lo que nunca debe hacer es ampliar el alcance de su transacción. Algunas personas sugirieron tomar el control de la transacción y revertirla después de las pruebas. Cuando haga esto, todas las entidades (suponiendo que esté utilizando el JPA) permanecerán unidas al contexto de persistencia durante la ejecución de la prueba. Esto puede dar lugar a algunos errores muy desagradables que son muy difíciles de encontrar .
En su lugar, debe borrar la base de datos manualmente después de cada prueba a través de una biblioteca como JPAUnit o similar a este enfoque (borrar todas las tablas usando JDBC).
Debido a sus problemas de rendimiento, no debe ejecutar las pruebas de integración en cada compilación. Deje que su servidor de integración continua haga esto. Si usa Maven, puede disfrutar del complemento a prueba de fallos que le permite separar sus pruebas en pruebas unitarias y de integración.
Además, no debes burlarte de nada. Recuerde que está probando la integración, es decir, probando el comportamiento dentro del entorno de ejecución en tiempo de ejecución.
fuente
Sobre escalabilidad
He tenido este problema algunas veces antes, que las pruebas de integración estaban tardando demasiado en ejecutarse y no eran prácticas para que un solo desarrollador se ejecute continuamente en un estricto ciclo de retroalimentación de cambios. Algunas estrategias para hacer frente a esto son:
Intente combinar estas técnicas para un mayor efecto.
fuente
Para fines de prueba, hemos utilizado una implementación basada en archivos de una base de datos SQLite (solo copie un recurso). Esto se hizo para que también pudiéramos probar las migraciones de esquema. Por lo que sé, los cambios de esquema no son transaccionales, por lo que no se revertirán después de abrogar una transacción. Además, no tener que depender del soporte de transacciones para la configuración de prueba permite probar el comportamiento de las transacciones desde su aplicación correctamente.
fuente