Mejores practicas para JUnit

Recurro a ustedes para preguntar cuales son las mejores practicas al momento de implementar test-cases con junit. Explico el entorno que tengo y el problema que encontre al comenzar con los test.

Mi aplicacion esta basada en spring, con daos para el acceso a base de datos e hibernate para la persistencia. Tengo configurada la transaccionalidad en todos los beans de servicio. Segui la doc de spring para la realizacion de pruebas con JUnit 4 y configurar la transaccionalidad en mis test. Todo fue bien salvo que como comento, encontre unos detalles que no supe como atacar de la mejor forma.

Segun entiendo, deben ser pruebas unitarias, en el sentido de que cada clase de test debe contener solo lo relacionado a una accion, que si por ejemplo tengo un catalogo de personas debo tener 3 clases de test, una para agregar, otra para editar y otra para eliminar, no? Eso crei en un principio pero cada clase carga su contexto de spring lo cual orita no es tan pesado, pero al ratito que tenga como 50 test este tiempo si seria tiempo muerto. Asi que como en junit te permite declarar varios test por clase, entonces los pupse en una sola clase. Ahora el detalle esta en que como es transaccional, desde la anotacion @Before @Test y @After de junit se ejecutan en la misma transaccion y por lo tanto no puedo ver la informacion persitida en la bd hasta que termina el metodo @After. Lo malo es que por defecto la transaccion es rollback y nunca veo los cambios. Sin embargo ese comportamiento se puede modificar en la clase test, e incluso sobreescribir por metodo, pero aun asi dependo de que termine el metodo @After para ver los cambios en la base de datos.

El problema que veo es que no puedo andar haciendo los chequeos en el metodo @AfterTransaction que spring marca para ejecutar despues del metodo @After, por que como sabran cada accion (insert, update, delete) requiere de chequeos propios, por lo que entonces caigo al esquema inicial, de que cada accion sea en una clase pero con el inconveniente de que en un futuro que sean 50 tests o mas, se vuelva demasiado lento.

La doc de spring marca que para obtener los cambios en la base sin esperar a que termine la transaccion hay que llamar el metodo flush de la session, sin embargo desde mi clase test no tengo acceso a ella y no quiero poner esa llamada en mis daos.

Entonces, la pregnta es, ¿como lo han resuelto ustedes?, ¿como aplican los test-case asus proyectos?

Saludos

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de luxspes

Depende

Lo de una clase por accion, depende, a veces es buena idea, a veces no, depende de lo complejo de cada, caso, si es solo un CRUD, probablmente con una clase sea suficiente (ahora que, por otro lado, si es solo un CRUD, quien sabe si tenga caso el esfuerso del test, a menos claro que lo veas mas que nada como un ejercicio)
Lo de las tracciones, pues si, el default es rollback, por que el chiste es que puedas correr el test todas las veces que quieras, y para eso, todo debe volver a las condiciones iniciales, si no imaginate que tienes un test para el alta de un nuevo usuario, y tienes una restriccion en la base de datos de que no puede haber 2 usuarios con le mismo nombre... la primera ves que lo corras todo va a estar bien, y la segunda va a fallar, y eso haria a tu test no repetible (y debe ser repetible). (Por otro lado, si la no repeticion del nombre de usuario es una caracteristica de tu sistema, deberias tener otro test en donde pruebes que al meter 2 usuarios con el mismo nombre mande el error correcto...)
La idea es que tu mismo verifique que los cambios se hayan efectuado correctamente antes de hacer el rollback (usando los Assert de JUnit)
Luego lo del flush de session... supongo que ahi ya no estamos hablando solo de Spring y JUnit, si no que ademas estas usando Hibernate?

Imagen de luxspes

Unitils

Tal ves el proyecto Unitils te pueda ayudar. Incluso manejan alguna ligas (a articulos en ingles) que explican algunos principios basicos de como hacer pruebas con Hibernate y Spring

Imagen de Nopalin

simon

si utilizo hibernate, eh ahi la cuestion.

Leyendo la documentacion de spring, la clase base de ayuda para hacer pruebas esa una que contiene un simpleJdbcTemplate, lo cual me parece perfecto, pues en cada prueba que haces debes comprobar que la informacion se guarde bien a la base y se recupere. El problema te digo es que si declaro transaccional el metodo test, y tengo varios accesos al dao, no los persiste hasta no terminar el metodo test, y si no los persiste no puedo comprobarlos con el jdbcTemplate.

Bueno, tal vez debo identificar donde realmente ocupo que sea transaccional y donde no.

saludos y gracias por la pagina

Imagen de luxspes

JUnit, Spring, Hibernate

Clase base para hacer pruebas? Desde Spring 2.5.x y JUnit 4.x no hay necesidad de clase base (hay una todavia, pero todo se puede configurar con annotations, y por lo tanto, la clase base es opcional)
Comprobar con el jdbcTemplate? por que con jdbcTemplate?... si tu aplicacion usa Hibernate, pues verifica con Hibernate! (A menos que quieras UnitTestear a Hibernate mismo, pero en ese caso harias mejor en descargar todos los tests que JBoss ya escribio para ese fin). Si estas trabajando con Spring, simplemente inyecta la session en tu clase de test con @Autowired (por cierto, por que estas usando todavia session... no deberias aprovechar el soporte para JPA que tiene Hibernate y usar EntityManager? o estas trabajando todavia con JDK 1.4, Spring 2.0 y Hibernate 2.0 ?

Imagen de luxspes

FlushMode

Recuerda que en Hibernate, hay diferentes "FlushMode" que son los que controlan en que momento se envia a la base de datos los cambios, si vas a comprobar tus tests con jdbcTemplate, necesitas que primero Hibernate mande los cambios a la base de datos... una opcion seria usar el mode ALWAYS, asi los cambios se persistiran inmediatamente (aunque en teoria deberias poder dehacerlos si todo esta ocurriendo dentro de una transacción) otra opcion es pedirle a Hibernate que haga un flush, eso enviar los cambios a la base de datos (que igual podras revertir siempre que llames al rollback de la transaccion). Finalmente, si utilizas AUTO , y haces tus comprobaciones con Hibernate (y no con jdbcTemplate) entonces Hibernate de forma "automatica" detectara que estas consultando una tabla para la que hay cambios "pendientes de flushear" y hara el flush de forma transparente para satisfacer tu consulta (e igualmemente Spring revertira todo al final, es importante recordar que Flush y las transacciones son acciones que pueden ir juntas, pero tambien pueden ser independientes)

Imagen de ezamudio

no vas a poder

Si el JdbcTemplate usa una conexión (porque lo creaste con un DataSource) y el DAO de Hibernate usa otra conexión (porque el SessionFactory usa ese mismo DataSource), entonces no vas a poder leer nada hasta el final de la transacción.

Si el DAO inserta a la base de datos, por mucho que haga un flush, será visible dentro de la transacción solamente, no para el resto de la app, hasta que se haga commit. El JdbcTemplate tendría que estar sincronizado para usar la misma transacción que el DAO de Hibernate (y por lo tanto la misma conexión).

Tal vez configurando la transacción al crearla para que permite READ_DIRTY (o algo asi se llama)...

Imagen de luxspes

Transaccion: Es cierto, hay que recordar el IsolationLevel

Tal ves podría usar el TransactionAwareDataSourceProxy para garantizar que todo ocurra con la misma transaccion/conexion. Pero sigo pensando que es mas facil si usa a Hibernate para sus asserts en ves de usar el JdbcTemplate

Imagen de Nopalin

sigo con hibernate

La razon porla que sigo utilizando la sesion de hibernate es por que su criteria api me parece estupenda, muy fácil de utilizar y entendible.

Y creo que tienes razon, no hay por que veirificar que hibernate persista la info a la base de datos, lo deberia checar con el mismo hibernate, que al final de cuentas ya ha sido probado que funciona, y en realidad este punto era la cuestion, persistir con hibernate y hacer las validaciones con un jdbc aparte.

Saludos y gracias por sus respuestas, no estoy acostumbrado a hacer test por que en un principio me parecian tontos jeje, pero bueno, uno aprende de sus errores.