Por el momento, nuestras pruebas están cargando varias veces la información de la base de datos y vamos a solucionar este problema creando una función que borre la información. Para esto, debemos usar la instrucción @After para que JUnit ejecute la función cada vez que termina de hacer un test.
Tests de JDBC: Búsqueda e Inserción de Películas por ID
En este caso puede ser util la anotación @BeforeClass en lugar del @Before, para que no estés creando y cerrando conexiones con cada uno de los tests sino que lo hagas por cada clase de Tests, te ahorras bastante en términos de máquina. Sin embargo, el @BeforeClass debe ir con un método estático.
Más info al respecto
Ahí el problema de hacer esto en este ejemplo, es que si conviertes el método setUp() a estático (que es lo que requiere la anotación @BeforeClass) también debes definir como variables estáticas a jdbcTemplate y a movieRepository, por lo cuál ni siquiera guarda correctamente la BD en memoria.
A mi también me pareció mejor el uso de @BeforeClass.
es importante el parámetro DB_CLOSE_DELAY para que no se borré la base entre llamadas a métodos (tests), sin este me daba el error "Caused by: org.h2.jdbc.JdbcSQLException: Table "MOVIES" not found;"
También lo que comenta @Cesar es cierto, pero me parece que el uso de JdbcTemplate es parte de la implementación de la clase y por ello lo moví a la clase MovieRepositoryJdbc.
Me ha pasado algo curioso con el orden de los test porque cuando ejecutaba uno iba mal porque primero insertaba la película del test y luego en el otro test de traer todas las películas fallaba porque faltaba una. Para corregirlo pueden utilizar una anotación llamada @Order(<Order number>) en el que indican explícitamente el orden de sus tests
package com.mael.javatests.movies.data;import com.mael.javatests.movies.model.Genre;import com.mael.javatests.movies.model.Movie;import org.junit.BeforeClass;import org.junit.Test;import org.springframework.core.annotation.Order;import org.springframework.core.io.ClassPathResource;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.datasource.DriverManagerDataSource;import org.springframework.jdbc.datasource.init.ScriptUtils;import javax.sql.DataSource;import java.sql.SQLException;import java.util.Arrays;import java.util.Collection;importstatic org.hamcrest.CoreMatchers.is;importstatic org.junit.Assert.*;publicclassMovieRepositoryImplTest{privatestaticMovieRepositoryImpl movieRepository; @BeforeClasspublicstaticvoidsetUp() throws Exception{//Creando una base de datos de prueba en memoria con h2DataSource dataSource =newDriverManagerDataSource("jdbc:h2:mem:test;MODE=MYSQL","sa","sa");//Ejecuto script para crear tabla de movies y unos datos de pruebaScriptUtils.executeSqlScript(dataSource.getConnection(),newClassPathResource("sql-scripts/test-data.sql"));//creo una clase JDBC template para enviarsela a mi repositorio de MovieJdbcTemplate jdbcTemplate =newJdbcTemplate(dataSource); movieRepository =newMovieRepositoryImpl(jdbcTemplate);} @Test @Order(1)publicvoidinsertMovie(){Movie movie =newMovie("Super 8",112,Genre.THRILLER); movieRepository.saveOrUpdate(movie);Movie movieExpected = movieRepository.findById(4L);assertThat(movieExpected,is(newMovie(4,"Super 8",112,Genre.THRILLER)));} @Test @Order(2)publicvoidloadAllMovies() throws SQLException{Collection<Movie> movies = movieRepository.findAll();assertThat(movies,is(Arrays.asList(newMovie(1,"Dark Knight",152,Genre.ACTION),newMovie(2,"Memento",113,Genre.THRILLER),newMovie(3,"Matrix",136,Genre.ACTION),newMovie(4,"Super 8",112,Genre.THRILLER))));} @Test @Order(3)publicvoidloadMovieById(){Movie movie = movieRepository.findById(2L);assertThat(movie,is(newMovie(2,"Memento",113,Genre.THRILLER)));}}
En este caso en particular puede ser útil este método, pero en aplicaciones reales donde el número de pruebas de integración es mucho mayor a mi parecer no es una muy buena idea
Me resulta muy interesante trabajar de esta manera, ya que es algo completamente nuevo para mí. Nunca antes había tenido la oportunidad de hacerlo de esta manera y estoy disfrutando mucho el proceso.
Pésimo curso la verdad :u, no he entendido nada utiliza JDBC, Spring cosas que no he visto siguiendo la ruta de Java, además de que el profesor programa super rápido, no lo recomiendo tomar
Llámenme de lento aprendizaje, pero en general, la ruta de Backend de Java en Platzi está muy mal 😦
He seguido casi toda la ruta y si eres nuevo en todo esto te despecha. A diferencia de otras rutas como la de Python o C#, en esta ruta en particular todos los profes lo hacen demasiado rápido como que acabe rápido el curso, no se explica nada de donde sale como asumiendo que ya todo se sabe, los proyectos nacen de la nada o ya a medio camino como en este caso, los componentes que se hablan están deprecated.
No era mas sencillo hacer una unión con el curso de Spring?
Era completamente inecesario usar Spring
No encontré la forma de realizar ++"shutdown"++ en la base de datos. 😢
Así se ejecuta
s.execute("shutdown");
Bueno, les comento que intenté lo del @BeforeAll (junit5) pero de igual forma el orden de ejecución de los tests fallaba el findAll porque encontraba en memoria el registro con id[4].
Intenté el @Order que mencionaron en un comentario pero tampoco funcionó.
Y la solución más sencilla de todas, dejando el @BeforeEach es agregar esta línea al principio del archivo "test-data.sql"
drop TABLEIFEXISTS movies;
Excelente curso!!! Me surge la siguiente inquietud cuando implementamos @After. El comando DROP no es muy peligroso si estamos testeando en una Base de Datos que ya contiene información y está funcionando?
Siempre es importante asegurarse de que los entornos de pruebas estén aislados de los entornos de producción para evitar el riesgo de borrar datos importantes al usar instrucciones como DROP.
Es que en una base que ya contiene datos y está funcionando solo haces la conexión (Oracle, MySQL, Postgres, etc.), no es necesario correr ningún script por lo que no es necesario una funcionalidad "After" como la hace el profesor (que borra los datos)
Bueno ya que tuve problemas con el video anterior, debo esperar me respondan para hacer este y lo que sigue.
Todo bien por aca :) gracias
Solo el alcance que vi en comentarios más abajo.. el Genre debe ser String :)
Igual se podría agregar el id a nuestro objeto movie, para no tener que hacer otro de nuevo en el assert
@TestpublicvoidinsertAMovie(){Movie movie =newMovie("Super 8",112,Genre.THRILLER); movieRepository.saveOrUpdate(movie);Movie movieFromDb = movieRepository.findById(4); movie.setId(4);assertEquals(movie, movieFromDb);}
queryForObject(String, Object, RowMapper) esta deprecado:
En vez de usar @After podríamos usar @BeforeClass para que el método donde se carga la base de datos solo se ejecute una vez antes de empezar a probar todos los test
MovieRepositoryJdbc.java
package com.platzi.javatests.movies.data;import com.platzi.javatests.movies.model.Genre;import com.platzi.javatests.movies.model.Movie;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import java.util.Collection;publicclassMovieRepositoryJdbcimplementsMovieRepository{privateJdbcTemplate jdbcTemplate;privatestaticRowMapper<Movie> movieMapper =(rs, rowNum)->newMovie( rs.getInt("id"), rs.getString("name"), rs.getInt("minutes"),Genre.valueOf(rs.getString("genre")));publicMovieRepositoryJdbc(JdbcTemplate jdbcTemplate){this.jdbcTemplate= jdbcTemplate;} @OverridepublicMoviefindById(long id){Object[] args ={ id };return jdbcTemplate.queryForObject("SELECT * FROM movies WHERE id = ?", args, movieMapper);} @OverridepublicCollection<Movie>findAll(){return jdbcTemplate.query("SELECT * FROM movies", movieMapper);} @OverridepublicvoidsaveOrUpdate(Movie movie){ jdbcTemplate.update("INSERT INTO movies(name, minutes, genre) VALUES(?, ?, ?)", movie.getName(), movie.getMinutes(), movie.getGenre().toString());}}
Hola al utilizar H2 y ejecutar los tests me da este error. Alguien sabe a que se deba.
A mi me esta dando error porque hace primero el segundo test el cual crea una pelicula por lo que falla el primero asi que en la web he visto que al ser una db en memoria se podria crear una nueva con un nombre diferente , uso junit 5
esto hace que ya no se use el after
Mi código:
publicclassMovieRepositoryImplTest{privateMovieRepositoryImpl movieRepository;privateDataSource dataSource;@BeforepublicvoidsetUp()throwsException{//1.- Creamos la conexión a la BD de pruebas dataSource =newDriverManagerDataSource("jdbc:h2:mem:test;MODE=MYSQL","sa","sa");//2.- Ejecutamos el script que se encuentra en la carpeta de resourcesScriptUtils.executeSqlScript(dataSource.getConnection(),newClassPathResource("sql-scripts/test.sql"));//3.- Inicializamos nuestros objetosJdbcTemplate jdbcTemplate =newJdbcTemplate(dataSource); movieRepository =newMovieRepositoryImpl(jdbcTemplate);}@TestpublicvoidloadAllMovies()throwsSQLException{//1.- Realizamos la búsquedaCollection<Movie> movies = movieRepository.findAll();//2.- TesteamosassertThat(movies,CoreMatchers.is(Arrays.asList(newMovie("Dark Knight",1,12,Genre.ACTION),newMovie("Memento",2,11,Genre.THRILLER),newMovie("Matrix",3,12,Genre.ACTION))));}@TestpublicvoidloadMovieById(){Movie movieTwo = movieRepository.findById(2);assertThat(movieTwo,CoreMatchers.is(newMovie("Memento",2,11,Genre.THRILLER)));}@TestpublicvoidinsertAMovie(){ movieRepository.saveOrUpdate(newMovie("Super 8",12,Genre.THRILLER));Movie movieActual = movieRepository.findById(4);assertEquals(newMovie("Super 8",4,12,Genre.THRILLER), movieActual);}@AfterpublicvoidtearDown()throwsException{try(Statement s = dataSource.getConnection().createStatement()){ s.execute("drop all objects delete files");}}}
Este curso es muy bueno la verdad 💚
gracias
recomiendo estudiar SQL antes de ver esta parte del curso, por que las sentencias de sql requieren un tiempo para incorporarlas.