La anotación @DataJpaTest tiene un comportamiento muy curioso e interesante, ya que nos permite probar el acceso a datos, protegiendo la base de datos haciendo un Rollback al final de cata test, por lo que es muy útil al probar los servicios en una aplicacion Spring Boot
En este articulo, vamos a realizar el test de la clase PeliculaService que montamos en el articulo/video anterior
Para facilitar los ejemplos, hemos decidido trabajar, pàra test, con una base de datos H2 en memoria y cargar 3 registros en el proceso de arranque, con el fichero (data.sql)
y ya iremos creando o borrando registros, segun nos interese, pero sabiendo que en el test siguiente, ese efecto habrá desaparecido y solo existirán estos tres registro que creamos al arranque…y sin modificar
Con eso, ya conseguimos disponer de una de las características que todo test ha de cumplir, que se puedan pasar las pruebas en cualquier orden y que no dependa una de otra.
A partir de eso, ya podemos empezar a escribir el test
@DataJpaTest
class PeliculaServiceTest {
@Autowired
private IPelicula peliculaRepository;
private PeliculaService peliculaServicio;
Pelicula pelicula;
Pelicula pelicula20;
@BeforeAll
static void setUpBeforeClass() throws Exception {
}
@BeforeEach
void setUp() throws Exception {
pelicula = new Pelicula(1, "La pelicula", 8);
pelicula20 = new Pelicula(20, "La gran pelicula", 1);
peliculaServicio = new PeliculaService(peliculaRepository);
}
Necesito que Spring me inserte el repositorio y el servicio, ya que para crear el servicio, necesito el repositorio, y creo asi mismo un paar de objetos «Pelicula» para ir utilizando en el test
Para el test de insert:
@Test
void testInsert() throws DomainException, DAOException {
Pelicula peli = peliculaServicio.insert(pelicula);
Pelicula peliDB = peliculaServicio.leerUno(peli.getId_pelicula()).get();
assertNotNull(peliDB);
assertEquals(pelicula.getPe_titulo(), peliDB.getPe_titulo());
assertEquals(pelicula.getPe_identificador(), peliDB.getPe_identificador());
}
inserto una de las peliculas creadas, y a continuacion,leo la pelicula con el id facilitado por la base de datos, y compruebo que los datos leídos son los mismos que tenia el objeto que grabé
Para el test de Update
@Test
void testUpdate() throws DomainException, DAOException {
Pelicula peli = peliculaServicio.insert(pelicula);
peli.setPe_titulo("La pelicula actualizada");
peliculaServicio.update(peli);
Pelicula peliDB = peliculaServicio.leerUno(peli.getId_pelicula()).get();
assertNotNull(peliDB);
assertEquals(peli.getPe_titulo(), peliDB.getPe_titulo());
assertEquals(peli.getPe_identificador(), peliDB.getPe_identificador());
}
Grabamos la película, le modificamos el título, y actualizamos la película, luego solo resta leerla y comparar que tiene el título modificado
El update con error es más sencillo
@Test
void testUpdateException() throws DomainException, DAOException {
assertThrows(DAOException.class, () -> peliculaServicio.update(pelicula));
}
Solo es necesario que intentemos actualizar un registro que no existe….
La prueba del patch es semejante, lo unico que debemos modificar un solo campo
@Test
void testPatch() throws DomainException, DAOException {
Pelicula peli = peliculaServicio.insert(pelicula);
peli.setPe_titulo("La pelicula actualizada");
peli.setPe_identificador(0);
peliculaServicio.patch(peli);
Pelicula peliDB = peliculaServicio.leerUno(pelicula.getId_pelicula()).get();
assertNotNull(peliDB);
assertEquals(peli.getPe_titulo(), peliDB.getPe_titulo());
assertEquals(pelicula.getPe_identificador(), peliDB.getPe_identificador());
}
Y eso hacemos. Insertamos una película y enviamos el patch con el título cambiado y el identificador a 0, luego deberemos comprobar que el registro leido tiene cambiado el título, pero no el identificador.
Ahora debemos probar las funciones de borrar utilizando un objeto Película,
@Test
void testBorrar() throws DomainException, DAOException {
peliculaServicio.insert(pelicula);
assertTrue(peliculaServicio.borrarPorId(pelicula.getId_pelicula()));
assertFalse(peliculaServicio.leerUno(pelicula.getId_pelicula()).isPresent());
}
@Test
void testBorrarException() throws DomainException, DAOException {
Pelicula peli = new Pelicula(999L, "no existe", 0);
assertThrows(DAOException.class, () -> peliculaServicio.borrar(peli));
}
Para el borrado correcto, insertamos una película, a continuación la borramos, esperando que devuelva true, y, por ultimo comprobamos que ya no existe, y, para el borrado erroneo, montamos un objeto Pelicula que sabemos no existe, y lo intentamos borrar, esperando recibir un DaoException…..
La prueba de borrar por Id, repetimos la jugada, pero esta vez trabajando solo con los id
@Test
void testBorrarPorId() throws DomainException, DAOException {
assertTrue(peliculaServicio.borrarPorId(20L));
assertFalse(peliculaServicio.existe(20L));
}
@Test
void testBorrarPorIdException() throws DomainException, DAOException {
assertThrows(DAOException.class, () -> peliculaServicio.borrarPorId(9999L));
}
Aquí, no nos molestamos en insertar, porque sabemos que durante la carga, se crearon los registros 20, 21 y 22, por lo que podemos dar la orden de borrado del 20, y comprobar que luego no existe, o intentar borrar el 9999 que no existe para recibir el DaoException
La prueba de ListarTodos, la podemos hacer contando los registros leídos
@Test
void testListTodos() {
List<Pelicula> peliculas = peliculaServicio.listarTodos();
assertNotNull(peliculas);
assertEquals(3, peliculas.size());
}
Sabemos que solo hay 3 porque en el fichero data.sql solo hemos escritos tres insert, facil,¿no?
El listarTodos con error, es un poco mas dificil, ya que la condición es que no haya registros, pues los borraremos.
@Test
void testListTodosException() {
// List<Pelicula> peliculasB = peliculaServicio.listarTodos();
// peliculasB.forEach(p -> {
// try {
// peliculaServicio.borrar(p);
// } catch (DAOException e) {
// }
// });
peliculaRepository.deleteAll();
List<Pelicula> peliculas = peliculaServicio.listarTodos();
assertNotNull(peliculas);
assertEquals(0, peliculas.size());
}
Tenemos comentadas las líneas para hacerlo manualmente, ya que JpaRepository me ofrece el método deleteAll que borra todo el contenido de golpe, perfecto, luego solo es necesario hacer la lectura, y comprobar que no recibo nada, aunque si tengo un List…
En la siguiente prueba, comprobamos el leerUno
@Test
void testLeerUno() throws DomainException, DAOException {
peliculaServicio.insert(pelicula20);
Pelicula peliDB = peliculaServicio.leerUno(pelicula20.getId_pelicula()).get();
assertNotNull(peliDB);
assertEquals(pelicula20.getId_pelicula(), peliDB.getId_pelicula());
assertEquals(pelicula20.getPe_titulo(), peliDB.getPe_titulo());
assertEquals(pelicula20.getPe_identificador(), peliDB.getPe_identificador());
}
@Test
void testLeerUnoException() {
assertFalse(peliculaServicio.leerUno(9999l).isPresent());
}
Para el correcto, podemos insertar uno, que es la solucion utilizada, o comprobar la lectura de los tres que existen ya en la tabla, y para el error, solo leer un id que sabemos no existe….
Para el ultimo caso, he simplificado al máximo
@Test
void testExiste() throws DomainException, DAOException {
assertTrue(peliculaServicio.existe(20L));
}
@Test
void testExisteFalso() throws DomainException, DAOException {
assertFalse(peliculaServicio.existe(9999L));
}
Elemental, verdad?
En mi caso, me prepare una pequeña rutina para revisar algun caso extraño, que lista todos los registros existentes. Debemos recordar que al trabajar con DataJpaTest, en cada test, SIEMPRE tengo los tres registros originales…
void verTabla(String titulo) {
System.out.println(titulo+" ----------------------");
List<Pelicula> peliculasB = peliculaServicio.listarTodos();
peliculasB.forEach(p -> {
System.out.println(p);
});
}
Y con esto, comprobamos la corrección de nuestro servicio de peliculas (PeliculaService), por lo que despues, cuando escribamos el controlador, nos podremos despreocupar de todo esto
Conclusión
Todo este desarrollo lo tendreis explicado con mas detalle en youTube, a partir de fin de mayo, y, aunque es conveniente que intentéis escribirlo TODO vosotros, si queréis renunciar a ello, lo teneis tambien en GitHub
Este desarrollo esta hecho para disponer de un fuente para explicar otros temas, tal y como se indica en Visión de conjunto con Spring