En nuestra aplicación de películas queremos poder buscar por varios atributos a la vez. Por ejemplo, películas de acción que duren menos de 2 horas. ¿Cómo podemos hacer esto?
En MovieService vamos a implementar un método como el siguiente:
En este caso, la película “template” que pasamos como argumento será una plantilla que puede tener varios atributos rellenados, para indicar por cuáles queremos buscar. Los atributos que tengan valor null no se usarán para la búsqueda.
Nota: Nos irá bien cambiar el tipo del atributo minutes a Integer en lugar de int, porque así podremos indicar un valor null. En Java, los tipos primitivos como int, boolean o double no pueden tener valor *null.
En un test podríamos buscar películas de acción de menos de 2h 30m de la siguiente manera:
String name =null;// no queremos buscar por nombreInteger minutes =150;// 2h 30mGenre genre =Genre.ACTION;Movie template =newMovie(name, minutes, genre);Collection<Movie> movies = movieService.findMoviesByTemplate(template);assertThat(getMovieIds(movies),is(Arrays.asList(7)));
Nota: El código anterior se podría reducir (haciendo inline de las variables). En los tests es conveniente hacer esto, pues suele quedar más claro. El código compactado quedaría así:
Piensa diferentes combinaciones de búsquedas y escribe tests para ellas. Por ejemplo, prueba a buscar por nombre y duración; por nombre y género; por nombre, género y duración; etc. Si añadiste el campo director (reto anterior), también prueba a buscar por director junto a otros atributos.
Si en la búsqueda indicamos el id de la película, se ignorarán los demás atributos, ya que al buscar por id no es necesario indicar ningún atributo más.
Si los minutos indicados son negativos, la función lanzará una IllegalArgumentException.
Les dejo mi aporte. De esta forma se crea una lista con las condiciones aplicables en base al template recibido y el código queda más elegante.
publicCollection<Movie>findByTemplate(Movie template){if(template.getId()!=null){Movie movie =findById(template.getId());return movie !=null?Collections.singletonList(movie):newArrayList<>();}if(template.getMinutes()<0){thrownewIllegalArgumentException("duration must be greater or equal than zero.");}List<Predicate<Movie>> filters =newArrayList<>();if(template.getName()!=null){ filters.add(movie -> movie.getName().toLowerCase().contains(template.getName().toLowerCase().trim()));}if(template.getMinutes()!=null){ filters.add(movie -> movie.getMinutes()<= template.getMinutes());}if(template.getGenre()!=null){ filters.add(movie -> movie.getGenre().equals(template.getGenre()));}if(template.getDirector()!=null){ filters.add(movie -> movie.getDirector().toLowerCase().contains(template.getDirector().toLowerCase().trim()));}return repository.findAll().stream().filter(movie -> filters.stream().allMatch(filter -> filter.test(movie))).collect(Collectors.toList());}
Aquí los tests
@Nested @DisplayName("when searching movies by multiple attributes")classSearchByTemplateMethod{ @Test @DisplayName("using id")voidwhenUsingId(){Collection<Movie> movies = movieService.findByTemplate(newMovie(5,null,null,Genre.ACTION,null));assertEquals(Collections.singletonList(5),getMovieIds(movies));} @Test @DisplayName("using negative minutes")voidwhenUsingNegativeMinutes(){assertThrows(IllegalArgumentException.class,()-> movieService.findByTemplate(newMovie(null,null,-15,Genre.ACTION,null)));} @Test @DisplayName("using genre and minutes")voidwhenUsingGenreAndMinutes(){Collection<Movie> movies = movieService.findByTemplate(newMovie(null,null,180,Genre.ACTION,null));assertEquals(Arrays.asList(1,5),getMovieIds(movies));} @Test @DisplayName("using name and minutes")voidwhenUsingNameAndMinutes(){Collection<Movie> movies = movieService.findByTemplate(newMovie(null,"annabelle",100,null,null));assertEquals(Collections.singletonList(7),getMovieIds(movies));} @Test @DisplayName("using director and minutes")voidwhenUsingDirectorAndMinutes(){Collection<Movie> movies = movieService.findByTemplate(newMovie(null,null,110,null,"Chris"));assertEquals(Collections.singletonList(4),getMovieIds(movies));}}
y los resultados:
Interesante como organizaste los tests y como usaste una clase nested 🤔
publicclassMovieService{privateMovieRepository movieRepository;publicMovieService(MovieRepository movieRepository){this.movieRepository= movieRepository;}publicCollection<Movie>findMoviesByGenre(Genre genre){return movieRepository.findAll().stream().filter(movie -> movie.getGenre()== genre).collect(Collectors.toList());}publicCollection<Movie>findMoviesByLength(Integer length){return movieRepository.findAll().stream().filter(movie -> movie.getMinutes()<= length).collect(Collectors.toList());}publicCollection<Movie>findMoviesByName(String name){StringNAME= name.toLowerCase();return movieRepository.findAll().stream().filter(movie -> movie.getName().toLowerCase().contains(NAME)).collect(Collectors.toList());}publicCollection<Movie>findMoviesByDirector(String director){StringDIRECTOR= director.toLowerCase();return movieRepository.findAll().stream().filter(movie -> movie.getDirector().toLowerCase().contains(DIRECTOR)).collect(Collectors.toList());}publicCollection<Movie>findMoviesByTemplate(Movie template){Collection<Movie> movies = movieRepository.findAll();if( template.getId()==null){ movies =templateFilter(template, movies);}else{ movies = movies.stream().filter(movie -> movie.getId()== template.getId()).collect(Collectors.toList());}return movies;}privateCollection<Movie>templateFilter(Movie template,Collection<Movie> movies){if( template.getName()!=null){ movies = movies.stream().filter(movie -> movie.getName().toLowerCase().contains(template.getName().toLowerCase())).collect(Collectors.toList());}if(template.getDirector()!=null){ movies = movies.stream().filter(movie -> movie.getDirector().toLowerCase().contains(template.getDirector().toLowerCase())).collect(Collectors.toList());}if(template.getMinutes()!=null){ movies = movies.stream().filter(movie -> movie.getMinutes()<= template.getMinutes()).collect(Collectors.toList());}if(template.getGenre()!=null){ movies = movies.stream().filter(movie -> movie.getGenre()== template.getGenre()).collect(Collectors.toList());}return movies;}}
Aquí la clase de pruebas MovieServiceShould:
publicclassMovieServiceShould{privateMovieService movieService; @BeforepublicvoidsetUp() throws Exception{MovieRepository movieRepository =Mockito.mock(MovieRepository.class);Mockito.when(movieRepository.findAll()).thenReturn(Arrays.asList(newMovie(1,"Dark Knight",152,Genre.ACTION,"Christopher Nolan"),newMovie(2,"Memento",113,Genre.THRILLER,"Christopher Nolan"),newMovie(3,"There's Something About Mary",119,Genre.COMEDY,"Peter Farrelly, Bobby Farrelly"),newMovie(4,"Super 8",112,Genre.THRILLER,"J. J. Abrams"),newMovie(5,"Scream",111,Genre.HORROR,"Wes Craven"),newMovie(6,"Home Alone",103,Genre.COMEDY,"Chris Columbus, Raja Gosnell, Peter Hewitt, Rod Daniel"),newMovie(7,"Matrix",136,Genre.ACTION,"Sisters Wachowski"),newMovie(8,"Dark Knight Rises",152,Genre.ACTION,"Christopher Nolan"),newMovie(9,"The Matrix Reloaded",136,Genre.ACTION,"Sisters Wachowski"),newMovie(10,"The Matrix Revolutions",136,Genre.ACTION,"Sisters Wachowski"),newMovie(11,"Star Wars: The Force Awakens",135,Genre.ACTION,"J. J. Abrams"))); movieService =newMovieService(movieRepository);} @Testpublicvoidreturn_movies_by_genre(){Collection<Movie> movies = movieService.findMoviesByGenre(Genre.COMEDY);assertThat(getMoviesIds(movies),is(Arrays.asList(3,6)));} @Testpublicvoidreturn_movies_by_length(){Collection<Movie> movies = movieService.findMoviesByLength(119);assertThat(getMoviesIds(movies),is(Arrays.asList(2,3,4,5,6)));} @Testpublicvoidreturn_movies_by_name(){Collection<Movie> movies = movieService.findMoviesByName("dark Knight");assertThat(getMoviesIds(movies),is(Arrays.asList(1,8)));} @Testpublicvoidreturn_movies_by_director(){Collection<Movie> movies = movieService.findMoviesByDirector("christopher Nolan");assertThat(getMoviesIds(movies),is(Arrays.asList(1,2,8)));}// MARK @Testpublicvoidreturn_movies_by_action_genre_and_2_and_30_hours_lenght(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie(null,150,Genre.ACTION));assertThat(getMoviesIds(movies),is(Arrays.asList(7,9,10,11)));} @Testpublicvoidreturn_movies_by_name_and_135_minutes_lenght(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie("the",135,null));assertThat(getMoviesIds(movies),is(Arrays.asList(3,11)));} @Testpublicvoidreturn_movies_by_name_and_genre(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie("the",null,Genre.COMEDY));assertThat(getMoviesIds(movies),is(Arrays.asList(3)));} @Testpublicvoidreturn_movies_by_name_by_action_genre_and_2_and_45_hours_lenght(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie("riseS",165,Genre.ACTION));assertThat(getMoviesIds(movies),is(Arrays.asList(8)));} @Testpublicvoidreturn_movies_by_director_and_2_and_30_hours_lenght(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie(null,null,150,null,"Nolan"));assertThat(getMoviesIds(movies),is(Arrays.asList(2)));} @Testpublicvoidreturn_movies_by_director_and_name(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie(null,"dark",null,null,"nolan"));assertThat(getMoviesIds(movies),is(Arrays.asList(1,8)));} @Testpublicvoidreturn_movies_by_director_and_thriller_genre(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie(null,null,null,Genre.THRILLER,"NolaN"));assertThat(getMoviesIds(movies),is(Arrays.asList(2)));} @Testpublicvoidreturn_movies_by_id_and_director(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie(4,null,null,null,"abrams"));assertThat(getMoviesIds(movies),is(Arrays.asList(4)));} @Testpublicvoidreturn_movies_by_id_and_2_and_30_hours_lenght(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie(6,null,150,null,null));assertThat(getMoviesIds(movies),is(Arrays.asList(6)));} @Testpublicvoidnegative_length(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie(null,-150,null));assertThat(getMoviesIds(movies),is(Arrays.asList()));}privateList<Integer>getMoviesIds(Collection<Movie> movies){return movies.stream().map(Movie::getId).collect(Collectors.toList());}}
Puntualizar que el método templateFilter() puede ser refactorizado, ya que está casi repitiendo el
mismo código que métodos como findMoviesByGenre(), findMoviesByLength(), findMoviesByName(), etc.
No encontré la manera de reutilizar esos métodos dentro de templateFilter().
Compañero Agradezco por darme una luz verde del ejercicio ,había logrado hacer el anterior , pero en este sí me sentía un poco perdido.
Muy valioso tu aporte.(Y)
Tengo que confesar que me costó y que el primer acercamiento me agobió bastante por no saber donde meterle mano jajajaja.
El reto resultó bastante desafiante.
A continuación dejo el código del resultado ya que tocó hacer cambios importantes al enum de Genre. De paso adicional encontré algo que se llama Predicate, que define un lambda de un condicional específico, Optional para definir valores a variables nulas y and para agrupar varios predicados, todas estas opciones disponibles para Java 8.
...publicclassMovieServiceImplimplementsMovieService{... @OverridepublicCollection<Movie>findMoviesByTemplate(Movie template){if(template.getMinutes()!=null&& template.getMinutes()<0){thrownewIllegalArgumentException();}Predicate<Movie> isName = n -> n.getName().toLowerCase().contains(Optional.ofNullable(template.getName()).orElse("").toLowerCase());Predicate<Movie> isMinutes = m -> m.getMinutes()<=Optional.ofNullable(template.getMinutes()).orElse(m.getMinutes());Predicate<Movie> isGenre = g -> g.getGenre().toString().contains(Optional.ofNullable(template.getGenre()).orElse(Genre.EMPTY).toString());Predicate<Movie> isDirector = d -> d.getDirector().toLowerCase().contains(Optional.ofNullable(template.getDirector()).orElse("").toLowerCase());return movieRepository.findAll().stream().filter(isName.and(isMinutes).and(isGenre).and(isDirector)).collect(Collectors.toList());}}
MovieServiceShould.java
...@RunWith(MockitoJUnitRunner.class)publicclassMovieServiceShould{... @TestpublicvoidreturnMoviesByMovieTemplateWithDurationAndGenre(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie(null,152,ACTION,null));assertThat(getMovieIds(movies),is(Arrays.asList(1,7)));} @TestpublicvoidreturnMoviesByMovieTemplateWithNameAndDuration(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie("Super 8",112,null,null));assertThat(getMovieIds(movies),is(Collections.singletonList(4)));} @TestpublicvoidreturnMoviesByMovieTemplateWithNameAndGenre(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie("Super 8",null,THRILLER,null));assertThat(getMovieIds(movies),is(Collections.singletonList(4)));} @TestpublicvoidreturnMoviesByMovieTemplateWithNameAndGenreAndDuration(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie("memento",113,THRILLER,null));assertThat(getMovieIds(movies),is(Collections.singletonList(2)));} @TestpublicvoidreturnMoviesByMovieTemplateWithDirector(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie(null,null,null,"nolan"));assertThat(getMovieIds(movies),is(Arrays.asList(1,2)));} @Test(expected =IllegalArgumentException.class)publicvoidreturnError_whenDurationIsNegative(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie(null,-120,null,null));assertThat(getMovieIds(movies),is(Arrays.asList(1,2)));}privateList<Integer>getMovieIds(Collection<Movie> movies){return movies.stream().map(Movie::getId).collect(Collectors.toList());}}
MovieRepositoryJdbc
publicCollection<Movie>findByTemplate(Movie movie){Collection<Movie> movies;StringBuilder parameters =newStringBuilder();List<Object> values =newArrayList<>();if(movie.getId()!=null&& movie.getId()>0){ movies =Arrays.asList(findById(movie.getId()));}else{if(movie.getName()!=null&&!movie.getName().isBlank()){ parameters.append(" NAME = ? "); values.add(movie.getName());}if(movie.getMinutes()!=null){if(movie.getMinutes()<0){thrownewIllegalArgumentException();} parameters.append((parameters.isEmpty()?"":" AND ")+" MINUTES = ? "); values.add(movie.getMinutes());}if(movie.getGenre()!=null){ parameters.append((parameters.isEmpty()?"":" AND ")+" GENRE = ? "); values.add(movie.getGenre().toString());}if(movie.getDirector()!=null){ parameters.append((parameters.isEmpty()?"":" AND ")+" DIRECTOR = ? "); values.add(movie.getDirector());} movies = jdbcTemplate.query("SELECT * FROM MOVIES WHERE "+ parameters, values.toArray(), movieMapper);}return movies;}}
MovieRepositoryJdbcTest
@Testvoidfind_movie_by_name_and_genre(){Collection<Movie> movies = movieRepository.findByTemplate(newMovie("Matrix",null,Genre.ACTION,null));assertThat(movies,CoreMatchers.is(Arrays.asList(newMovie(3,"Matrix",136,Genre.ACTION,"Director 2"))));} @Testvoidfind_movie_by_name_and_duration(){Collection<Movie> movies = movieRepository.findByTemplate(newMovie("Memento",113,null,null));assertThat(movies,CoreMatchers.is(Arrays.asList(newMovie(2,"Memento",113,Genre.THRILLER,"Director 1"))));} @Testvoidfind_movie_by_name_genre_and_duration(){Collection<Movie> movies = movieRepository.findByTemplate(newMovie("Memento",113,Genre.THRILLER,null));assertThat(movies,CoreMatchers.is(Arrays.asList(newMovie(2,"Memento",113,Genre.THRILLER,"Director 1"))));} @Testvoidfind_movie_by_genre_and_director(){Collection<Movie> movies = movieRepository.findByTemplate(newMovie(null,null,Genre.ACTION,"Director 2"));assertThat(movies,CoreMatchers.is(Arrays.asList(newMovie(3,"Matrix",136,Genre.ACTION,"Director 2"))));} @Testvoidfind_movie_negative_minutes(){assertThrows(IllegalArgumentException.class,()-> movieRepository.findByTemplate(newMovie(null,-100,Genre.ACTION,"Director 2")));}
La ultima parte del reto IllegalArgumentException
En la clase MovieService.java, en el método findMoviesByTemplate.
Quedaria si:
if(template.getMinutes()!=null&& template.getMinutes()>0){ movies = movies.stream().filter(movie -> movie.getMinutes()<= template.getMinutes()).collect(Collectors.toList());}elsethrownewIllegalArgumentException("Id can't be 0 or negative");
Y en la clase MovieServiceShould.java, en el método return_movies_by_length_negative.
Quedaria si:
@Test(expected =IllegalArgumentException.class)publicvoidreturn_movies_by_length_negative(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie(null,-119,Genre.COMEDY));}
Les comparto como genere el query para tomar los datos que obtenemos del template
@OverridepublicCollection<Movie>findMoviesByTemplate(Movie template){StringBuilder strBuilder =newStringBuilder("select * from movies where 1=1 ");List<Object> args =newArrayList<>();if(template.getId()!=null){ strBuilder.append("and id = ? "); args.add(template.getId());}if(template.getName()!=null){ strBuilder.append("and lower(name) like ? "); args.add(String.format("%%%s%%", template.getName().toLowerCase()));}if(template.getGenre()!=null){ strBuilder.append("and genre = ? "); args.add(template.getGenre().name());}if(template.getMinutes()>0){ strBuilder.append("and minutes <= ? "); args.add(template.getMinutes());}if(template.getDirector()!=null){ strBuilder.append("and lower(director) like ? "); args.add(String.format("%%%s%%", template.getDirector().toLowerCase()));}return jdbcTemplate.query(strBuilder.toString(), args.toArray(), movieMapper);}
publicCollection<Movie>findMoviesByTemplate(Movie template){if(template.getId()!=null){returnCollections.singletonList(this.movieRepository.findById((long)template.getId()));}return movieRepository.findAll().stream().filter( movie -> template.getName()==null|| movie.getName().toLowerCase().contains(template.getName().toLowerCase())).filter( movie -> template.getMinutes()==null|| movie.getMinutes()<= template.getMinutes()).filter( movie -> template.getGenre()==null|| movie.getGenre()== template.getGenre()).collect(Collectors.toList());}
METODO
publicCollection<Movie>findMoviesByTemplate(Movie template){// Si todos los atributos del objeto Movie son null, devuelve una lista vacíaif(template.getMinutes()==null&& template.getName()==null&& template.getGenre()==null){returnnewArrayList<>();}// Si el atributo ID no es null, devuelve una lista de películas cuyo ID coincide con el ID de la plantillaif(template.getId()!=null){return movieRepo.findAll().stream().filter(movie -> movie.getId()== template.getId()).collect(Collectors.toList());}// Si los minutos de la plantilla son null o menores o iguales a cero, lanza una excepciónif(template.getMinutes()==null|| template.getMinutes()<=0){thrownewIllegalArgumentException("Movie time can't be null and must be greater than 0 minutes");}// Filtra todas las películas del repositorio según los atributos de la plantilla// Si un atributo de la plantilla es null, se ignora ese atributo al filtrar las películas// Si un atributo de la plantilla no es null, solo las películas con el mismo valor para ese atributo pasan el filtroList<Movie> filteredMovies = movieRepo.findAll().stream().filter(movie ->{if(template.getMinutes()!=null&& movie.getMinutes()> template.getMinutes()){returnfalse;}if(template.getName()!=null&&!movie.getName().equals(template.getName())){returnfalse;}if(template.getGenre()!=null&&!movie.getGenre().equals(template.getGenre())){returnfalse;}returntrue;// Si la película pasa todos los filtros, se incluye en la lista filtrada}).collect(Collectors.toList());return filteredMovies;}
TEST
@Testpublicvoidreturn_movies_by_template_with_title_and_duration(){String name ="The Avengers";Integer minutes =143;Genre genre =null;// no buscamos por generoMovie template =newMovie(name, minutes, genre);Collection<Movie> movies = movieservice.findMoviesByTemplate(template);assertEquals(getIntegers(movies),Arrays.asList(1));} @Testpublicvoidreturn_movies_by_template_with_duration_and_genre(){String name =null;// no buscamos por nombreInteger minutes =100;Genre genre =Genre.COMEDY;Movie template =newMovie(name, minutes, genre);Collection<Movie> movies = movieservice.findMoviesByTemplate(template);assertEquals(getIntegers(movies),Arrays.asList(2));} @Test(expected =IllegalArgumentException.class)publicvoidreturn_movies_by_template_with_title_and_genre(){String name ="The Hangover";Integer minutes =null;// no buscamos por duraciónGenre genre =Genre.COMEDY;Movie template =newMovie(name, minutes, genre);```
publicCollection<Movie>findMoviesByTemplate(Movie template) throws UnsupportedOperationException{List<Integer> moviesIds = movieRepository.findAll().stream().map(movie -> movie.getId()).collect(Collectors.toList());if(template.getId()!=null){ moviesIds.retainAll(findMoviesById(template.getId()).stream().map(movie -> movie.getId()).collect(Collectors.toList()));}if(template.getDirector()!=null){ moviesIds.retainAll(findByDirector(template.getDirector()).stream().map(movie -> movie.getId()).collect(Collectors.toList()));}if(template.getGenre()!=null){ moviesIds.retainAll(findMoviesByGenre(template.getGenre()).stream().map(movie -> movie.getId()).collect(Collectors.toList()));}if(template.getMinutes()!=null&&template.getMinutes()>=0){ moviesIds.retainAll(findMoviesByLength(template.getMinutes()).stream().map(movie -> movie.getId()).collect(Collectors.toList()));}else{thrownewIllegalArgumentException("Los minutos indicados no pueden ser negativos");}if(template.getName()!=null){ moviesIds.retainAll(findByName(template.getName()).stream().map(movie -> movie.getId()).collect(Collectors.toList()));}Collection<Movie> movieTemplate =newArrayList<Movie>();for(int i =0; i < moviesIds.size(); i++){ movieTemplate.addAll(findMoviesById(moviesIds.get(i)));}return movieTemplate;}
Asi quedo uno de los metodos mas interesantes que eh hecho
publicCollection<Movie>findMovieByTemplate(Movie movieTemplate){ int atributosNonNull =0;/*Primero vamos a comprobar el numero de atributos no nulos
* que tenemos en el template*/if(movieTemplate.getName()!=null){ atributosNonNull++;}if(movieTemplate.getGenre()!=null){ atributosNonNull++;}if(movieTemplate.getMinutes()!=null){ atributosNonNull++;}/**Luego instanciaremos el objeto movies que son todas las peliculas que tenemos y el ArrayList<>() En donde meteremos las que nos regresen</>*/Collection<Movie> movies = movieRepositoryJdbc.findAll();Collection<Movie> moviesByTemplate =newArrayList<>();for(Movie movie : movies){/**Aqui implementaremos lo primero que nada que es que si se busca por id pues no mames, ya la tienes, el id es unico*/if(movieTemplate.getId()!=null&&Objects.equals(movieTemplate.getId(), movie.getId())){ moviesByTemplate.add(movie);}else{String error ="";switch(atributosNonNull){case1:if(movieTemplate.getName()!=null){ error ="el nombre";//Aqui creo que podria reutilizar el codigo de find movieByNameif(movie.getName().toLowerCase().contains(movieTemplate.getName().toLowerCase())){ moviesByTemplate.add(movie);}}//En esta seccion comprobamos el genero de la peliculaelseif(movieTemplate.getGenre()!=null&&Objects.equals(movieTemplate.getGenre(), movie.getGenre())){ error ="el genero"; moviesByTemplate.add(movie);}//Finalmente comprobamos la duracion o los minutos que tiene la pelicula, aqui deberia lanzar una excepcion pero ya me ocupare de esoelseif(movieTemplate.getMinutes()!=null){ error ="la duración";if(movieTemplate.getMinutes()<0){thrownewIllegalArgumentException("Los minutos no pueden ser un número negativo");}elseif(movieTemplate.getMinutes()>= movie.getMinutes()){ moviesByTemplate.add(movie);}}break;case2://Primero la convinacion nombre generoif(movieTemplate.getName()!=null&& movieTemplate.getGenre()!=null){ error ="el nombre y el genero";//Ahora comprobamos el nombre y el generoif(movie.getName().toLowerCase().contains(movieTemplate.getName().toLowerCase())&&Objects.equals(movieTemplate.getGenre(), movie.getGenre())){ moviesByTemplate.add(movie);}}//Ahora el Nombre y la duracionelseif(movieTemplate.getName()!=null&& movieTemplate.getMinutes()!=null){//Aqui comprobamos el nombreif(movie.getName().toLowerCase().contains(movieTemplate.getName().toLowerCase())){//Aqui la excepcion de illegal Argumentif(movieTemplate.getMinutes()<0){thrownewIllegalArgumentException("Los minutos no pueden ser un número negativo");//Y acá los minutos}elseif(movieTemplate.getMinutes()>= movie.getMinutes()){ moviesByTemplate.add(movie);}}}//Ahora el genero y los minutoselseif(movieTemplate.getGenre()!=null&& movieTemplate.getMinutes()!=null){//Aca comprobamos el generoif(Objects.equals(movieTemplate.getGenre(), movie.getGenre())){//Aqui la excepcion de illegal Argumentif(movieTemplate.getMinutes()<0){thrownewIllegalArgumentException("Los minutos no pueden ser un número negativo");//Y acá los minutos}elseif(movieTemplate.getMinutes()>= movie.getMinutes()){ moviesByTemplate.add(movie);}}}break;//Ahora lo haremos con los tres atributos, nombre, genero y minutoscase3://Primero el nombre y el generoif(movie.getName().toLowerCase().contains(movieTemplate.getName().toLowerCase())&&Objects.equals(movieTemplate.getGenre(), movie.getGenre())){//Y ahora los minutosif(movieTemplate.getMinutes()<0){thrownewIllegalArgumentException("Los minutos no pueden ser un número negativo");//Y acá los minutos}elseif(movieTemplate.getMinutes()>= movie.getMinutes()){ moviesByTemplate.add(movie);}}break;}}}return moviesByTemplate;}
Asi quedaron mis pruebas
/*-----------------Pruebas con un solo atributo-----------------*/ @Testpublicvoidget_movie_by_template_when_id_is_not_null(){Movie movie =newMovie(6,null,null,null);Collection<Movie> moviesGetted = movieService.findMovieByTemplate(movie);List<Integer> moviesids =getIntegers(moviesGetted);assertThat(moviesids,CoreMatchers.is(List.of(6)));} @Testpublicvoidget_movie_by_template_only_with_name(){Movie movie =newMovie(null,"super",null,null);Collection<Movie> moviesGetted = movieService.findMovieByTemplate(movie);List<Integer> moviesids =getIntegers(moviesGetted);assertThat(moviesids,CoreMatchers.is(Arrays.asList(1,3,4,6)));} @Testpublicvoidget_movie_by_template_only_with_minutes(){Movie movie =newMovie(null,null,112,null);Collection<Movie> moviesGetted = movieService.findMovieByTemplate(movie);List<Integer> moviesids =getIntegers(moviesGetted);assertThat(moviesids,CoreMatchers.is(Arrays.asList(4,5,6)));} @Testpublicvoidget_movie_by_template_only_with_genre(){Movie movie =newMovie(null,null,null,Genre.THRILLER);Collection<Movie> moviesGetted = movieService.findMovieByTemplate(movie);List<Integer> moviesids =getIntegers(moviesGetted);assertThat(moviesids,CoreMatchers.is(Arrays.asList(2,4)));}/*-----------------Pruebas con dos atributos-----------------*/ @Testpublicvoidget_movie_by_template_with_name_and_genre(){Movie movie =newMovie(null,"e",null,Genre.THRILLER);Collection<Movie> moviesGetted = movieService.findMovieByTemplate(movie);List<Integer> moviesids =getIntegers(moviesGetted);assertThat(moviesids,CoreMatchers.is(Arrays.asList(2,4)));} @Testpublicvoidget_movie_by_template_with_name_and_minutes(){Movie movie =newMovie(null,"er",152,null);Collection<Movie> moviesGetted = movieService.findMovieByTemplate(movie);List<Integer> moviesids =getIntegers(moviesGetted);assertThat(moviesids,CoreMatchers.is(Arrays.asList(1,3,4,6)));} @Testpublicvoidget_movie_by_template_with_genre_and_minutes(){Movie movie =newMovie(null,null,119,Genre.COMEDY);Collection<Movie> moviesGetted = movieService.findMovieByTemplate(movie);List<Integer> moviesids =getIntegers(moviesGetted);assertThat(moviesids,CoreMatchers.is(Arrays.asList(3,6)));}/*-----------------Pruebas con los tres atributos-----------------*/ @Testpublicvoidget_movie_by_template_with_name_minutes_and_genre(){Movie movie =newMovie(null,"Super 8",112,Genre.THRILLER);Collection<Movie> moviesGetted = movieService.findMovieByTemplate(movie);List<Integer> moviesids =getIntegers(moviesGetted);assertThat(moviesids,CoreMatchers.is(List.of(4)));}/*-----------------Pruebas cuando se den minutos negativos-----------------*/ @Testpublicvoidget_movie_by_template_with_negative_minutes_with_assertThrows(){Movie movie =newMovie(null,null,-1,null);assertThrows(IllegalArgumentException.class,()->{Collection<Movie> moviesGetted = movieService.findMovieByTemplate(movie);});} @Testpublicvoidget_movie_by_template_with_negative_minutes_with_asserEquals(){Movie movie =newMovie(null,null,-1,null);IllegalArgumentException exception =assertThrows(IllegalArgumentException.class,()->{Collection<Movie> moviesGetted = movieService.findMovieByTemplate(movie);});assertEquals("Los minutos no pueden ser un número negativo", exception.getMessage());}privatestaticList<Integer>getIntegers(Collection<Movie> movies){return movies.stream().map(Movie::getId).collect(Collectors.toList());}
Y este es el resultado
Me eh quedado ah gusto y esto lo usare en un proyecto que madre mia, me viene de perlas para buscar usuarios por nombre, id, rango o distintas convinaciones, esto sera genial jaja
Gracias Platzi, nunca me creí capaz de algo tan genial pero ahora solo veo que esto es genial pero el limite es el cielo jaja
En mi solución creé un método para consultar en la BDD según los datos del template.
Nuevo método en MovieRepositoryJdbc
@OverridepublicCollection<Movie>findByTemplate(Movie template){String and ="";List<Object> argsList =newArrayList<>();StringBuilder sql =newStringBuilder(); sql.append("select * from movies where ");if(template.getMinutes()!=null){ sql.append(" minutes <= ? "); argsList.add(template.getMinutes()); and =" and ";}if(StringUtil.isNotEmpty(template.getName())){ sql.append(and).append(" name like ? "); argsList.add("%"+template.getName()+"%"); and =" and ";}if(template.getGenre()!=null){ sql.append(and).append(" genre = ? "); argsList.add(template.getGenre().toString()); and =" and ";}if(StringUtil.isNotEmpty(template.getDirector())){ sql.append(and).append(" director like ? "); argsList.add("%"+template.getDirector()+"%");}return jdbcTemplate.query(sql.toString(), argsList.stream().toArray(), movieMapper);}
En la clase MovieService valido el tiempo negativo para lanzar la excepcion o si se debe consultar por id o por template
publicCollection<Movie>findMoviesByTemplate(Movie template) throws IllegalArgumentException{if(template.getMinutes()!=null&& template.getMinutes().intValue()<0){thrownewIllegalArgumentException("Time is negative");}if(template.getId()!=null){returnArrays.asList(findById(template.getId()));}else{return movieRepository.findByTemplate(template);}}
Nuevos test MovieRepositoryIntegrationTest con algunas combinaciones del template
@TestpublicvoidfindTemplateById(){Movie template =newMovie(5,"Scream",111,Genre.HORROR,"Kevin Williamson and Wes Craven");Collection<Movie> movies = movieRepositoryJdbc.findByTemplate(template);Assert.assertThat(movies,is(Arrays.asList(template)));} @TestpublicvoidfindTemplateByNameAndTime(){List<Movie> moviesList =Arrays.asList(newMovie(3,"There is Something about Mary",119,Genre.COMEDY,"Peter Farrelly"),newMovie(7,"Matrix",136,Genre.ACTION,"Lana and Lilly Wachowski"));Collection<Movie> movies = movieRepositoryJdbc.findByTemplate(newMovie(null,"MA",140,null,null));Assert.assertThat(movies,is(moviesList));} @TestpublicvoidfindTemplateByNameAndGenre(){List<Movie> moviesList =Arrays.asList(newMovie(7,"Matrix",136,Genre.ACTION,"Lana and Lilly Wachowski"),newMovie(8,"Batman vs Super Man",151,Genre.ACTION,"Zack Snyder"));Collection<Movie> movies = movieRepositoryJdbc.findByTemplate(newMovie(null,"mA",null,Genre.ACTION,null));Assert.assertThat(movies,is(moviesList));} @TestpublicvoidfindTemplateByTimeGenreAndDirector(){Collection<Movie> movies = movieRepositoryJdbc.findByTemplate(newMovie(null,160,Genre.ACTION,"noLA"));assertThat(movies,is(Arrays.asList(newMovie(1,"Dark Knight",152,Genre.ACTION,"Christopher Nolan"))));}
Nuevos test del MovieServiceTest
@Test(expected =IllegalArgumentException.class)publicvoidfindByTimeNegative(){ movieService.findMoviesByTemplate(newMovie(1,"Dark Night",-152,Genre.ACTION,"Christopher Nolan"));} @TestpublicvoidfindTemplateById(){Collection<Movie> movies = movieService.findMoviesByTemplate(newMovie(1,"Dark Night",152,Genre.ACTION,"Christopher Nolan"));assertThat(getId(movies),is(Arrays.asList(1)));} @TestpublicvoidfindTemplateByNameAndTime(){Movie template =newMovie(null,"MA",140,null,null);List<Movie> moviesList =Arrays.asList(newMovie(3,"There is Something about Mary",119,Genre.COMEDY,"Peter Farrelly"),newMovie(7,"Matrix",136,Genre.ACTION,"Lana and Lilly Wachowski"));Mockito.when(movieRepository.findByTemplate(template)).thenReturn(moviesList);assertThat(getId(movieService.findMoviesByTemplate(template)),is(Arrays.asList(3,7)));} @TestpublicvoidfindTemplateByNameAndGenre(){Movie template =newMovie(null,"supER",null,Genre.ACTION,null);List<Movie> moviesList =Arrays.asList(newMovie(4,"Super 8",112,Genre.ACTION,"Jeffrey Jacob Abrams"),newMovie(8,"Batman vs Super Man",151,Genre.ACTION,"Zack Snyder"));Mockito.when(movieRepository.findByTemplate(template)).thenReturn(moviesList);assertThat(getId(movieService.findMoviesByTemplate(template)),is(Arrays.asList(4,8)));} @TestpublicvoidfindTemplateByTimeGenreAndDirector(){Movie template =newMovie(null,"supER",null,Genre.ACTION,null);List<Movie> moviesList =Arrays.asList(newMovie(1,"Dark Knight",152,Genre.ACTION,"Christopher Nolan"));Mockito.when(movieRepository.findByTemplate(template)).thenReturn(moviesList);assertThat(getId(movieService.findMoviesByTemplate(template)),is(Arrays.asList(1)));}
También creé el método isNotEmpty en la clase StringUtil
Y por último los datos de prueba del archivo test-data.sql
CREATETABLEIFNOTEXISTSmovies( id INTAUTO_INCREMENTPRIMARYKEY, name VARCHAR(50)NOTNULL, minutes INTNOTNULL, genre VARCHAR(50)NOTNULL, director VARCHAR(50)NOTNULL);insert into movies(name, minutes, genre, director)values('Dark Knight',152,'ACTION','Christopher Nolan'),('Memento',113,'THRILLER','Christopher Nolan'),('There is Something about Mary',119,'COMEDY','Peter Farrelly'),('Super 8',112,'THRILLER','Jeffrey Jacob Abrams'),('Scream',111,'HORROR','Kevin Williamson and Wes Craven'),('Home Alone',102,'COMEDY','Chris Columbus'),('Matrix',136,'ACTION','Lana and Lilly Wachowski'),('Batman vs Super Man',151,'ACTION','Zack Snyder');
Mi solución, solo me falto agregar el IllegalArgumentException
publicCollection<Movie>findByTemplate(Movie template){Collection<Movie> leakedMovies =newArrayList<Movie>();Collection<Movie> movies =newArrayList<Movie>();Integer id = template.getId();String name = template.getName();Integer minutes = template.getMinutes();Genre genre = template.getGenre();if(id !=null)returnfindById(id);if(name !=null){ movies =findByName(name);for(Movie movie : movies){if(minutes !=null|| genre !=null){if(minutes !=null){if(movie.getMinutes()<= minutes) leakedMovies.add(movie);}if(genre !=null){if(genre == movie.getGenre()) leakedMovies.add(movie);}}else leakedMovies.add(movie);}}elseif(minutes !=null){ movies =findMoviesByLength(minutes);for(Movie movie : movies){if(genre !=null){if(genre == movie.getGenre()) leakedMovies.add(movie);}else leakedMovies.add(movie);}}elseif(genre !=null){ movies =findMoviesByGenre(genre); leakedMovies.addAll(movies);}return leakedMovies;}