Curso Básico de Testing en Java

Curso Básico de Testing en Java

Ferran Maylinch Carrasco

Ferran Maylinch Carrasco

Reto 4: Búsqueda por varios atributos

23/24

Lectura

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:

public Collection<Movie> findMoviesByTemplate(Movie template) { … }

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 nombre
Integer minutes = 150; // 2h 30m
Genre genre = Genre.ACTION;
Movie template = new Movie(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í:

assertThat(
 getMovieIds(movieService.findMoviesByTemplate(
	new Movie(null, 150, Genre.ACTION))),
  is(Arrays.asList(.......))
);

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.

Aportes 34

Preguntas 0

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

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.

  public Collection<Movie> findByTemplate(Movie template) {

        if (template.getId() != null) {
            Movie movie = findById(template.getId());
            return movie != null ? Collections.singletonList(movie) : new ArrayList<>();
        }

        if (template.getMinutes() < 0) {
            throw new IllegalArgumentException("duration must be greater or equal than zero.");
        }

        List<Predicate<Movie>> filters = new ArrayList<>();

        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")
    class SearchByTemplateMethod {

        @Test
        @DisplayName("using id")
        void whenUsingId() {
            Collection<Movie> movies = movieService.findByTemplate(new Movie(5, null, null, Genre.ACTION, null));
            assertEquals(Collections.singletonList(5), getMovieIds(movies));
        }

        @Test
        @DisplayName("using negative minutes")
        void whenUsingNegativeMinutes() {
            assertThrows(
                    IllegalArgumentException.class,
                    () -> movieService.findByTemplate(new Movie(null, null, -15, Genre.ACTION, null))
            );
        }

        @Test
        @DisplayName("using genre and minutes")
        void whenUsingGenreAndMinutes() {
            Collection<Movie> movies = movieService.findByTemplate(new Movie(null, null, 180, Genre.ACTION, null));
            assertEquals(Arrays.asList(1, 5), getMovieIds(movies));
        }

        @Test
        @DisplayName("using name and minutes")
        void whenUsingNameAndMinutes() {
            Collection<Movie> movies = movieService.findByTemplate(new Movie(null, "annabelle", 100, null, null));
            assertEquals(Collections.singletonList(7), getMovieIds(movies));
        }

        @Test
        @DisplayName("using director and minutes")
        void whenUsingDirectorAndMinutes() {
            Collection<Movie> movies = movieService.findByTemplate(new Movie(null, null, 110, null, "Chris"));
            assertEquals(Collections.singletonList(4), getMovieIds(movies));
        }
    }

y los resultados:

Aquí dejo mi reto:
Clase Movie:

public class Movie {

    private Integer id;
    private String name;
    private Integer minutes;
    private Genre genre;
    private String director;

    public Movie(String name, Integer minutes, Genre genre) {
        this(null, name, minutes, genre);
    }

    public Movie(Integer id, String name, Integer minutes, Genre genre) {
        this.id = id;
        this.name = name;
        this.minutes = minutes;
        this.genre = genre;
        this.director = "";
    }

    public Movie(Integer id, String name, Integer minutes, Genre genre, String director) {
        this.id = id;
        this.name = name;
        this.minutes = minutes;
        this.genre = genre;
        this.director = director;
    }


    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Integer getMinutes() {
        return minutes;
    }

    public Genre getGenre() {
        return genre;
    }

    public String getDirector() { return director; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Movie movie = (Movie) o;
        return minutes == movie.minutes &&
                Objects.equals(id, movie.id) &&
                Objects.equals(name, movie.name) &&
                genre == movie.genre;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, minutes, genre);
    }
}

Clase MovieService:

public class MovieService {

    private MovieRepository movieRepository;

    public MovieService(MovieRepository movieRepository) {
        this.movieRepository = movieRepository;
    }

    public Collection<Movie> findMoviesByGenre(Genre genre) {
        return movieRepository.findAll().stream()
                .filter(movie -> movie.getGenre() == genre).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByLength(Integer length) {
        return movieRepository.findAll().stream()
                .filter(movie -> movie.getMinutes() <= length).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByName(String name){
        String NAME = name.toLowerCase();
        return movieRepository.findAll().stream().filter(movie -> movie.getName().toLowerCase().contains(NAME)).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByDirector(String director) {
        String DIRECTOR = director.toLowerCase();
        return movieRepository.findAll().stream().filter(movie -> movie.getDirector().toLowerCase().contains(DIRECTOR)).collect(Collectors.toList());
    }

    public Collection<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;
    }

    private Collection<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:

public class MovieServiceShould {

    private MovieService movieService;

    @Before
    public void setUp() throws Exception {
        MovieRepository movieRepository = Mockito.mock(MovieRepository.class);

        Mockito.when(movieRepository.findAll()).thenReturn(
                Arrays.asList(
                        new Movie(1, "Dark Knight", 152, Genre.ACTION, "Christopher Nolan"),
                        new Movie(2, "Memento", 113, Genre.THRILLER, "Christopher Nolan"),
                        new Movie(3, "There's Something About Mary", 119, Genre.COMEDY, "Peter Farrelly, Bobby Farrelly"),
                        new Movie(4, "Super 8", 112, Genre.THRILLER, "J. J. Abrams"),
                        new Movie(5, "Scream", 111, Genre.HORROR, "Wes Craven"),
                        new Movie(6, "Home Alone", 103, Genre.COMEDY, "Chris Columbus, Raja Gosnell, Peter Hewitt, Rod Daniel"),
                        new Movie(7, "Matrix", 136, Genre.ACTION, "Sisters Wachowski"),
                        new Movie(8, "Dark Knight Rises", 152, Genre.ACTION, "Christopher Nolan"),
                        new Movie(9, "The Matrix Reloaded", 136, Genre.ACTION, "Sisters Wachowski"),
                        new Movie(10, "The Matrix Revolutions", 136, Genre.ACTION, "Sisters Wachowski"),
                        new Movie(11, "Star Wars: The Force Awakens", 135, Genre.ACTION,"J. J. Abrams")
                )
        );

        movieService = new MovieService(movieRepository);
    }

    @Test
    public void return_movies_by_genre() {

        Collection<Movie> movies = movieService.findMoviesByGenre(Genre.COMEDY);
        assertThat(getMoviesIds(movies), is(Arrays.asList(3, 6)) );
    }

    @Test
    public void return_movies_by_length(){

        Collection<Movie> movies = movieService.findMoviesByLength(119);
        assertThat(getMoviesIds(movies), is(Arrays.asList(2, 3, 4, 5, 6)) );
    }

    @Test
    public void return_movies_by_name() {

        Collection<Movie> movies = movieService.findMoviesByName("dark Knight");
        assertThat(getMoviesIds(movies), is(Arrays.asList(1, 8)));
    }

    @Test
    public void return_movies_by_director() {

        Collection<Movie> movies = movieService.findMoviesByDirector("christopher Nolan");
        assertThat(getMoviesIds(movies), is(Arrays.asList(1, 2, 8)));
    }
// MARK
    @Test
    public void return_movies_by_action_genre_and_2_and_30_hours_lenght() {
        
        Collection<Movie> movies =
                movieService.findMoviesByTemplate(new Movie(null, 150, Genre.ACTION));
        assertThat(getMoviesIds(movies), is(Arrays.asList(7, 9, 10, 11)) );
    }

    @Test
    public void return_movies_by_name_and_135_minutes_lenght() {

        Collection<Movie> movies =
                movieService.findMoviesByTemplate(new Movie("the", 135, null));
        assertThat(getMoviesIds(movies), is(Arrays.asList(3, 11)) );
    }

    @Test
    public void return_movies_by_name_and_genre() {

        Collection<Movie> movies =
                movieService.findMoviesByTemplate(new Movie("the", null, Genre.COMEDY));
        assertThat(getMoviesIds(movies), is(Arrays.asList(3)) );
    }

    @Test
    public void return_movies_by_name_by_action_genre_and_2_and_45_hours_lenght() {

        Collection<Movie> movies =
                movieService.findMoviesByTemplate(new Movie("riseS", 165, Genre.ACTION));
        assertThat(getMoviesIds(movies), is(Arrays.asList(8)) );
    }

    @Test
    public void return_movies_by_director_and_2_and_30_hours_lenght() {

        Collection<Movie> movies =
                movieService.findMoviesByTemplate(new Movie(null, null, 150, null, "Nolan"));
        assertThat(getMoviesIds(movies), is(Arrays.asList(2)) );
    }

    @Test
    public void return_movies_by_director_and_name() {

        Collection<Movie> movies =
                movieService.findMoviesByTemplate(new Movie(null, "dark", null, null, "nolan"));
        assertThat(getMoviesIds(movies), is(Arrays.asList(1, 8)) );
    }

    @Test
    public void return_movies_by_director_and_thriller_genre() {

        Collection<Movie> movies =
                movieService.findMoviesByTemplate(new Movie(null, null, null, Genre.THRILLER, "NolaN"));
        assertThat(getMoviesIds(movies), is(Arrays.asList(2)) );
    }

    @Test
    public void return_movies_by_id_and_director() {

        Collection<Movie> movies =
                movieService.findMoviesByTemplate(new Movie(4, null, null, null, "abrams"));
        assertThat(getMoviesIds(movies), is(Arrays.asList(4)) );
    }

    @Test
    public void return_movies_by_id_and_2_and_30_hours_lenght() {

        Collection<Movie> movies =
                movieService.findMoviesByTemplate(new Movie(6, null, 150, null, null));
        assertThat(getMoviesIds(movies), is(Arrays.asList(6)) );
    }

    @Test
    public void negative_length() {

        Collection<Movie> movies =
                movieService.findMoviesByTemplate(new Movie(null, -150, null));
        assertThat(getMoviesIds(movies), is(Arrays.asList()) );
    }


    private List<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().

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());
            } else throw new IllegalArgumentException("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)
    public void return_movies_by_length_negative() {
        Collection<Movie> movies = movieService.findMoviesByTemplate(new Movie(null, -119, Genre.COMEDY));
    }

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.

Movie.java

public class Movie {
  ...
  private Integer minutes;
  private Genre genre;
  private String director;

  public Movie(String name, Integer minutes, Genre genre, String director) {
    this(null, name, minutes, genre, director);
  }

  public Movie(Integer id, String name, Integer minutes, Genre genre, String director) {
    this.id = id;
    this.name = name;
    this.minutes = minutes;
    this.genre = genre;
    this.director = director;
  }

  ...

  public Integer getMinutes() {
    return minutes;
  }

  ...

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Movie movie = (Movie) o;
    return minutes.equals(movie.minutes) &&
        Objects.equals(id, movie.id) &&
        Objects.equals(name, movie.name) &&
        genre == movie.genre &&
        director.equals(movie.director);
  }
}

Genre.java

package com.platzi.javatests.movies.model;

public enum Genre {
  ACTION, COMEDY, DRAMA, HORROR, THRILLER, EMPTY;

  @Override
  public String toString() {
    return this == EMPTY ? "" : this.name();
  }
}

MovieService.java

package com.platzi.javatests.movies.service;

import com.platzi.javatests.movies.model.Genre;
import com.platzi.javatests.movies.model.Movie;

import java.util.Collection;

public interface MovieService {
	...
  Collection<Movie> findMoviesByTemplate(Movie template);
}

MovieServiceImpl.java

...
public class MovieServiceImpl implements MovieService {
	...

  @Override
  public Collection<Movie> findMoviesByTemplate(Movie template) {
    if (template.getMinutes() != null && template.getMinutes() < 0) {
      throw new IllegalArgumentException();
    }

    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)
public class MovieServiceShould {
  ...
  @Test
  public void returnMoviesByMovieTemplateWithDurationAndGenre() {
    Collection<Movie> movies =
      movieService.findMoviesByTemplate(
        new Movie(null, 152, ACTION, null)
      );

    assertThat(getMovieIds(movies), is(Arrays.asList(1, 7)));
  }

  @Test
  public void returnMoviesByMovieTemplateWithNameAndDuration() {
    Collection<Movie> movies =
      movieService.findMoviesByTemplate(
        new Movie("Super 8", 112, null, null)
      );

    assertThat(getMovieIds(movies), is(Collections.singletonList(4)));
  }

  @Test
  public void returnMoviesByMovieTemplateWithNameAndGenre() {
    Collection<Movie> movies =
      movieService.findMoviesByTemplate(
          new Movie("Super 8", null, THRILLER, null)
      );

    assertThat(getMovieIds(movies), is(Collections.singletonList(4)));
  }

  @Test
  public void returnMoviesByMovieTemplateWithNameAndGenreAndDuration() {
    Collection<Movie> movies =
      movieService.findMoviesByTemplate(
          new Movie("memento", 113, THRILLER, null)
      );

    assertThat(getMovieIds(movies), is(Collections.singletonList(2)));
  }

  @Test
  public void returnMoviesByMovieTemplateWithDirector() {
    Collection<Movie> movies =
      movieService.findMoviesByTemplate(
        new Movie(null, null, null, "nolan")
      );

    assertThat(getMovieIds(movies), is(Arrays.asList(1, 2)));
  }

  @Test(expected = IllegalArgumentException.class)
  public void returnError_whenDurationIsNegative() {
    Collection<Movie> movies =
      movieService.findMoviesByTemplate(
        new Movie(null, -120, null, null)
      );

    assertThat(getMovieIds(movies), is(Arrays.asList(1, 2)));
  }

  private List<Integer> getMovieIds(Collection<Movie> movies) {
    return movies.stream().map(Movie::getId).collect(Collectors.toList());
  }
}


En mi solución creé un método para consultar en la BDD según los datos del template.

Nuevo método en MovieRepositoryJdbc

    @Override
    public Collection<Movie> findByTemplate(Movie template) {

        String and = "";
        List<Object> argsList = new ArrayList<>();
        StringBuilder sql =  new StringBuilder();
        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

  public Collection<Movie> findMoviesByTemplate(Movie template) throws IllegalArgumentException {

        if (template.getMinutes() != null && template.getMinutes().intValue() < 0) {
            throw new IllegalArgumentException("Time is negative");
        }

        if (template.getId() != null) {
           return Arrays.asList(findById(template.getId()));
        } else {
            return movieRepository.findByTemplate(template);
        }
    }

Nuevos test MovieRepositoryIntegrationTest con algunas combinaciones del template

    @Test
    public void findTemplateById() {
        Movie template = new Movie(5, "Scream", 111, Genre.HORROR, "Kevin Williamson and Wes Craven");
        Collection<Movie> movies = movieRepositoryJdbc.findByTemplate(template);
        Assert.assertThat(movies, is(Arrays.asList(template)));
    }

    @Test
    public void findTemplateByNameAndTime() {

        List<Movie> moviesList = Arrays.asList(
                new Movie(3, "There is Something about Mary", 119, Genre.COMEDY, "Peter Farrelly"),
                new Movie(7, "Matrix", 136, Genre.ACTION, "Lana and Lilly Wachowski")
        );

        Collection<Movie> movies = movieRepositoryJdbc.findByTemplate(new Movie(null, "MA", 140, null, null));
        Assert.assertThat(movies, is(moviesList));
    }

    @Test
    public void findTemplateByNameAndGenre() {

        List<Movie> moviesList = Arrays.asList(
                new Movie(7, "Matrix", 136, Genre.ACTION, "Lana and Lilly Wachowski"),
                new Movie(8, "Batman vs Super Man", 151, Genre.ACTION, "Zack Snyder")
        );

        Collection<Movie> movies = movieRepositoryJdbc.findByTemplate(new Movie(null, "mA", null, Genre.ACTION, null));
        Assert.assertThat(movies, is(moviesList));
    }


    @Test
    public void findTemplateByTimeGenreAndDirector() {
        Collection<Movie> movies = movieRepositoryJdbc.findByTemplate(new Movie(null, 160, Genre.ACTION, "noLA"));
        assertThat(movies, is(Arrays.asList(
                new Movie(1, "Dark Knight", 152, Genre.ACTION, "Christopher Nolan")
        )));
    }

Nuevos test del MovieServiceTest

@Test(expected = IllegalArgumentException.class)
    public void findByTimeNegative() {
        movieService.findMoviesByTemplate(new Movie(1, "Dark Night", -152, Genre.ACTION, "Christopher Nolan"));
    }

    @Test
    public void findTemplateById() {
        Collection<Movie> movies = movieService.findMoviesByTemplate(new Movie(1, "Dark Night", 152, Genre.ACTION, "Christopher Nolan"));
        assertThat(getId(movies), is(Arrays.asList(1)));
    }

    @Test
    public void findTemplateByNameAndTime() {

        Movie template = new Movie(null, "MA", 140, null, null);
        List<Movie> moviesList = Arrays.asList(
                new Movie(3, "There is Something about Mary", 119, Genre.COMEDY, "Peter Farrelly"),
                new Movie(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)));
    }

    @Test
    public void findTemplateByNameAndGenre() {

        Movie template = new Movie(null, "supER", null, Genre.ACTION, null);
        List<Movie> moviesList = Arrays.asList(
                new Movie(4, "Super 8", 112, Genre.ACTION, "Jeffrey Jacob Abrams"),
                new Movie(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)));
    }

    @Test
    public void findTemplateByTimeGenreAndDirector() {
        Movie template = new Movie(null, "supER", null, Genre.ACTION, null);
        List<Movie> moviesList = Arrays.asList(
                new Movie(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

   public static boolean isNotEmpty(String str) {
        return !isEmpty(str);
    }

    public static boolean isEmpty(String str) {
        return str == null || str.trim().length() == 0;
    }

Y por último los datos de prueba del archivo test-data.sql

CREATE TABLE IF NOT EXISTS movies (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  minutes INT NOT NULL,
  genre VARCHAR(50) NOT NULL,
  director VARCHAR(50) NOT NULL
);

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

public Collection<Movie> findByTemplate(Movie template) {
    Collection<Movie> leakedMovies = new ArrayList<Movie>();
    Collection<Movie> movies = new ArrayList<Movie>();

    Integer id = template.getId();
    String name = template.getName();
    Integer minutes = template.getMinutes();
    Genre genre = template.getGenre();

    if (id != null) return findById(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);
        }
    } else if (minutes != null) {
        movies = findMoviesByLength(minutes);
        for (Movie movie : movies) {
            if (genre != null) {
                if (genre == movie.getGenre())
                    leakedMovies.add(movie);
            }
            else leakedMovies.add(movie);
        }
    } else if (genre != null) {
        movies = findMoviesByGenre(genre);
        leakedMovies.addAll(movies);
    }
    
    return leakedMovies;
}

MovieService

    public Collection<Movie> findMoviesByTemplate(Movie template) {
        return movieRepository.findAll().stream()
                .filter(movie -> {
                    if ((template.getId()!=null)&&(template.getId() != movie.getId())){
                        return false;
                    }
                    if ((template.getGenre()!=null)&&(template.getGenre() != movie.getGenre())){
                        return false;
                    }
                    if ((template.getName()!=null)&&(template.getName() != movie.getName())){
                        return false;
                    }
                    if ((template.getMinutes()!=0)&&(template.getMinutes() != movie.getMinutes())){
                        return false;
                    }
                    if ((template.getDirector()!=null)&&(template.getDirector() != movie.getDirector())){
                        return false;
                    }
                    return true;
                }).collect(Collectors.toList());
    }

MovieServiceShould

    @Test
    public void return_by_template() {
        Movie movie =  new Movie(1, "Dark Knight", 152, Genre.ACTION,"director1");
        Collection<Movie> movies = movieService.findMoviesByTemplate(movie);
        assertThat(movies, CoreMatchers.is(Arrays.asList(
                movie
        )) );
    }

Reto 4:

public Collection<Movie> findMoviesByTemplate(Movie template){
        return movieRepository.findAll().stream().filter(
                movie -> {
                    if(template.getId() != null){
                        return movie.getId().equals(template.getId());
                    }else{
                        if(template.getMinutes() < 0){
                            throw new IllegalArgumentException("La duración de una pelicula, no puede ser negativo");
                        }else if(movie.getMinutes() <= template.getMinutes()){
                            if(template.getDirector() != null){
                                if(movie.getDirector().toLowerCase().contains(template.getDirector().toLowerCase())){
                                    return true;
                                }
                            }
                            if(template.getGenre() != null){
                                if(movie.getGenre().equals(template.getGenre())){
                                    return true;
                                }
                            }
                            if(template.getName() != null){
                                if(movie.getName().toLowerCase().contains(template.getName().toLowerCase())){
                                    return true;
                                }
                            }
                        }
                    }
                    return false;
                }
        ).collect(Collectors.toList());
    }

Reto Cumplido.
Me puso a pensar y literal obtuve un primer resultado que da la solución pero no es el mas optimo y no esta tan limpio el código, mi siguiente reto será optimizarlo y mejorarlo y si no me es posible buscar otra forma de implementarlo que sea de la mejor manera. No siendo más mis solución fue:

MovieService.java

 /**
     * Metodo del Reto para buscar una pelicula dado ciertos atributos
     * @param template pelicula modelo a buscar
     * @return una lista de peliculas que cumplen las condiciones de template
     */
    public Collection<Movie> findMoviesByTemplatePrueba(Movie template) {
        if (template.getName() != null){
            //Filtramos por nombre
            if (template.getMinutes() != null){
                //Filtramos por nombre y minutos
                if (template.getGenre() != null){
                    //Filtramos por nombre, duración y genero
                    return movieRepository.findAll().stream()
                            .filter(movie -> movie.getName().contains(template.getName()) &&
                                            (movie.getMinutes() <= template.getMinutes()) &&
                                    movie.getGenre().equals(template.getGenre())
                            ).collect(Collectors.toList());
                } else {
                    //Filtramos por nombre y duración
                    return movieRepository.findAll().stream()
                            .filter(movie -> movie.getName().contains(template.getName()) &&
                                    (movie.getMinutes() <= template.getMinutes())
                            ).collect(Collectors.toList());
                }
            } else {
                //Filtramos por nombre
                return findMovieByName(template.getName());
            }
        } else {
            if (template.getMinutes() != null){
                if (template.getGenre() != null){
                    //Filtramos por duracion y genero
                    return movieRepository.findAll().stream()
                            .filter(movie ->(movie.getMinutes() <= template.getMinutes()) &&
                                    movie.getGenre().equals(template.getGenre())
                            ).collect(Collectors.toList());
                }
                else {
                    //Filtramos por duración
                    return findMoviesByDuration(template.getMinutes());
                }
            } else{
                if (template.getGenre() != null){
                    //Filtramos por Genero
                    return findMoviesByGenre(template.getGenre());
                }
                else{
                    throw new IllegalArgumentException("Todos los parametros del template son nulos");
                }
            }
        }
    }

y el montaje de los tests

//Test creados para el reto de busqueda por plantilla

    @Test
    public void return_movies_with_template_and_by_properties_duration_and_genre() {
        assertThat(
            getMovieIds(movieService.findMoviesByTemplatePrueba(
                    new Movie(null, 150, Genre.ACTION))),
                CoreMatchers.is(Arrays.asList(7)));

    }

    @Test
    public void return_movies_with_template_and_by_properties_name_and_duration() {
        assertThat(
                getMovieIds(movieService.findMoviesByTemplatePrueba(
                        new Movie("ome", 105, null))),
                CoreMatchers.is(Arrays.asList(6)));
    }

    @Test
    public void  return_movies_with_template_and_by_properties_name_and_genre() {
        assertThat(
                getMovieIds(movieService.findMoviesByTemplatePrueba(
                        new Movie("ome", null,  Genre.COMEDY))),
                CoreMatchers.is(Arrays.asList(3,6)));
    }
    @Test
    public void  return_movies_with_template_and_by_properties_name_duration_and_genre() {
        assertThat(
                getMovieIds(movieService.findMoviesByTemplatePrueba(
                        new Movie("me", 150,  Genre.THRILLER))),
                CoreMatchers.is(Arrays.asList(2)));
    }

    @Test(expected = IllegalArgumentException.class)
    public void return_IllegalArgumentException_because_all_properties_are_null() {
        Collection<Movie> movies = movieService.findMoviesByTemplatePrueba(
                new Movie(null, null,  null));
    }

Solo agrego método findByTemplate(). Los test tienen la misma base que los realizados en el curso. El código me quedó redundante por el tema de los NullPointerExceptions.

public Collection<Movie> findMovieByTemplate(Movie tamplate) {

        Integer movieId = tamplate.getId();
        String movieName = tamplate.getName();
        Integer movieLength = tamplate.getMinutes();
        Genre movieGenre = tamplate.getGenre();
        String movieDirector = tamplate.getDirector();


        if ((!Objects.isNull(movieId) && movieId < 0 ) || (!Objects.isNull(movieLength) && movieLength < 0)) {
            throw new IllegalArgumentException("Negative numbers are not permit");
        }

        Collection<Movie> movieResult = new LinkedList<>();

        if (!Objects.isNull(movieId)) {
            Movie myMovie;

            try {
                myMovie = movieRepository.findById(movieId);
            } catch (Exception e) {
                throw new EmptyStackException();
            }

            movieResult.add(myMovie);

            return movieResult;
        }

        Stream<Movie> movieStream = null;

        if (!Objects.isNull(movieName)) {
            if (Objects.isNull(movieStream)) {
                movieStream = findMoviesByName(movieName).stream();
            } else {
                movieStream = movieStream.filter(movie -> movie.getName().toLowerCase(Locale.ROOT).
                        contains(movieName.toLowerCase(Locale.ROOT)));
            }
        }

        if (!Objects.isNull(movieLength)) {
            if (Objects.isNull(movieStream)) {
                movieStream = findMoviesByLength(movieLength).stream();
            } else {
                movieStream = movieStream.filter(movie -> movie.getMinutes() <= movieLength);
            }
        }

        if (!Objects.isNull(movieGenre)) {
            if (Objects.isNull(movieStream)) {
                movieStream = findMoviesByGenre(movieGenre).stream();
            } else {
                movieStream = movieStream.filter(movie -> movie.getGenre() == movieGenre);
            }
        }

        if (!Objects.isNull(movieDirector)) {
            if (Objects.isNull(movieStream)) {
                movieStream = findMovieByDirector(movieDirector).stream();
            } else {
                movieStream = movieStream.filter(movie -> movie.getDirector().toLowerCase(Locale.ROOT).
                        contains(movieDirector.toLowerCase(Locale.ROOT)));
            }
        }

        if (Objects.isNull(movieStream)) return null;

        movieResult = movieStream.collect(Collectors.toList());
        return movieResult;
    }

//Clase Movie.java : aquí se cambio el tipo de dato int por integer

package com.platzi.javatests.movies.model;

public class Movie {

private Integer id;
private String name;
private Integer minutes;
private Genre genre;
private String director;

public Movie(String name, Integer minutes, Genre genre,String director) {
    this(null, name, minutes, genre,director);
}

public Movie(Integer id, String name, Integer minutes, Genre genre,String director) {
    this.id = id;
    this.name = name;
    this.minutes = minutes;
    this.genre = genre;
    this.director = director;
}

public Integer getId() {
    return id;
}

public String getName() {
    return name;
}

public Integer getMinutes() {
    return minutes;
}

public Genre getGenre() {
    return genre;
}

public String getDirector() {
    return director;
}

}

//Clase MoviceService.java : aquí se hace un filtro de búsqueda por template y se definió el método

public Collection<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;

}

public Collection<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().equals(template.getId())).collect(Collectors.toList());
    }

    return movies;
}

//Clase MovieServiceShould.java: aquí se busco por minutos, genero y director.

package com.platzi.javatests.movies.service;

import com.platzi.javatests.movies.data.MovieRepository;
import com.platzi.javatests.movies.model.Genre;
import com.platzi.javatests.movies.model.Movie;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import static org.junit.Assert.*;

public class MovieServiceShould {

private MovieService movieService;

@Before
public void setUp() throws Exception {

    MovieRepository movieRepository = Mockito.mock(MovieRepository.class);

    Mockito.when(movieRepository.findAll()).thenReturn(
            Arrays.asList(
                    new Movie(1, "Dark Knight", 152, Genre.ACTION,"director1"),
                    new Movie(2, "Memento", 113, Genre.THRILLER,"director2"),
                    new Movie(3, "There's Something About Mary", 119, Genre.COMEDY,"director3"),
                    new Movie(4, "Super 8", 112, Genre.THRILLER,"director4"),
                    new Movie(5, "Scream", 111, Genre.HORROR,"director5"),
                    new Movie(6, "Home Alone", 103, Genre.COMEDY,"director6"),
                    new Movie(7, "Matrix", 136, Genre.ACTION,"director7"),
                    new Movie(8, "SUPERMAN", 121, Genre.ACTION,"director8"),
                    new Movie(9, "supergirl", 144, Genre.ACTION,"director19")
            )
    );

    movieService = new MovieService(movieRepository);
}

@Test
public void return_movie_by_template() {

    String name = null;
    Integer minutes = 162 ;
    Genre genre = Genre.ACTION;
    String director = "director1" ;
    Movie template = new Movie(name, minutes, genre,director);
    Collection<Movie> movies =
            movieService.findMoviesByTemplate(template);
    assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(1,9)));
}

Comparto mi solución (mejorable con mas tiempo)

public class MovieService {

    private MovieRepository movieRepository;

    public MovieService(MovieRepository movieRepository) {
        this.movieRepository = movieRepository;
    }

    public Collection<Movie> findMoviesByGenre(Genre genre) {

        return movieRepository.findAll().stream()
                .filter(movie -> movie.getGenre() == genre).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByLength(Integer length) {
        if (length != null && length < 0) {
            throw new IllegalArgumentException("negative values not allowed");
        } else {
            return movieRepository.findAll().stream()
                    .filter(movie -> movie.getMinutes() <= length).collect(Collectors.toList());
        }
    }

    public Collection<Movie> findMoviesByName(String name) {
        if (name == null) return null;
        return movieRepository.findAll().stream()
                .filter(movie -> movie.getName().toLowerCase().contains(name.toLowerCase())).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByTemplate(Movie template) {

        Collection<Movie> movies = movieRepository.findAll();

        if (template.getId() == null) {
            if (template.getMinutes() != null && template.getMinutes() < 0) {
                throw new IllegalArgumentException("negative values not allowed");
            } else {
                movies = templateFilter(template, movies);
            }
        } else {
            movies = movies.stream().filter(movie -> movie.getId() == template.getId()).collect(Collectors.toList());
        }

        return movies;
    }

    private Collection<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.getMinutes() != null && template.getMinutes() >= 0) {
            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;
    }
}
public class MovieServiceTest {
    private MovieService movieService;
    DriverManagerDataSource dataSource;

    @Before
    public void setUp() throws Exception {

        dataSource = new DriverManagerDataSource("jdbc:h2:mem:test;MODE=MYSQL", "sa", "sa");
        ScriptUtils.executeSqlScript(dataSource.getConnection(), new ClassPathResource("sql-scripts/test-data.sql"));
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        MovieRepositoryJdbc movieRepository = new MovieRepositoryJdbc(jdbcTemplate);
        movieService = new MovieService(movieRepository);
    }

    @Test
    public void find_movies_by_name() {

        Collection<Movie> movies = movieService.findMoviesByName("Man");

        assertThat( movies, is(Arrays.asList(
                new Movie(4, "Superman", 120, Genre.ACTION),
                new Movie(5, "Man of steel", 134, Genre.ACTION),
                new Movie(7, "Iron Man", 121, Genre.ACTION)
        )) );
    }

    @Test
    public void find_movies_by_minutes() {

        Collection<Movie> movies = movieService.findMoviesByLength(103);

        assertThat( movies, is(Arrays.asList(
                new Movie(6, "Home Alone", 103, Genre.COMEDY)
        )) );
    }

    @Test(expected = IllegalArgumentException.class)
    public void find_movies_by_negative_minutes() {

        Collection<Movie> movies = movieService.findMoviesByLength(-103);

        assertThat( movies, is(Arrays.asList()) );
    }

    @Test
    public void find_movies_by_name_null() {

        Collection<Movie> movies = movieService.findMoviesByName(null);

        assertThat(movies, nullValue());
    }

    @Test
    public void find_movies_by_genre_and_minutes() {
        assertThat(
                getMoviesIds(movieService.findMoviesByTemplate(
                        new Movie(null, 150, Genre.ACTION))),
                is(Arrays.asList(3, 4, 5, 7)));
    }

    @Test
    public void find_movies_by_genre_and_name() {
        assertThat(
                getMoviesIds(movieService.findMoviesByTemplate(
                        new Movie("man", null, Genre.ACTION))),
                is(Arrays.asList(4, 5, 7)));
    }

    @Test
    public void find_movies_by_name_and_minutes() {
        assertThat(
                getMoviesIds(movieService.findMoviesByTemplate(
                        new Movie("ME", 119, null))),
                is(Arrays.asList(2, 6)));
    }

    @Test
    public void find_movies_by_name_and_minutes_and_genre() {
        assertThat(
                getMoviesIds(movieService.findMoviesByTemplate(
                        new Movie("ME", 119, Genre.COMEDY))),
                is(Arrays.asList(6)));
    }

    @Test(expected = IllegalArgumentException.class)
    public void find_movies_by_name_and_negative_minutes_and_genre() {
        assertThat(
                getMoviesIds(movieService.findMoviesByTemplate(
                        new Movie("ME", -119, Genre.COMEDY))),
                is(Arrays.asList(6)));
    }

    @Test
    public void find_movies_with_complete_template() {
        assertThat(
                getMoviesIds(movieService.findMoviesByTemplate(
                        new Movie( 3,"La entrevista", 119, Genre.COMEDY))),
                is(Arrays.asList(3)));
    }

    @Test
    public void find_movies_with_template_and_id() {
        assertThat(
                getMoviesIds(movieService.findMoviesByTemplate(
                        new Movie( 6,null, null, null))),
                is(Arrays.asList(6)));
    }

    private List<Integer> getMoviesIds(Collection<Movie> movies) {
        return movies.stream().map(Movie::getId).collect(Collectors.toList());
    }

    @After
    public void tearDown() throws Exception {

        // Remove H2 files -- https://stackoverflow.com/a/51809831/1121497
        final Statement s = dataSource.getConnection().createStatement();
        s.execute("drop all objects delete files"); // "shutdown" is also enough for mem db
    }
}
CREATE TABLE IF NOT EXISTS movies (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  minutes INT NOT NULL,
  genre VARCHAR(50) NOT NULL
);

insert into movies (name, minutes, genre) values
    ('Dark Knight', 152, 'ACTION'),
    ('Memento', 113, 'THRILLER'),
    ('Matrix', 136, 'ACTION'),
    ('Superman', 120, 'ACTION'),
    ('Man of steel', 134, 'ACTION'),
    ('Home Alone', 103, 'COMEDY'),
    ('Iron Man', 121, 'ACTION'),
    ('SEVEN', 115, 'THRILLER');

Reto completado:

MovieService.java

    public Collection<Movie> templateFilter(Movie template, Collection<Movie> movies) {

        if (template.getName() != null) {
            // remove a movie
            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;
    }

    public Collection<Movie> findMoviesByTemplate(Movie template) {


        Collection<Movie> movies = movieRepository.findAll();

        if (template.getId() == null) {
//        movie(null, 150, Genere.ACTION)
            movies = templateFilter(template, movies);
        } else {
//        movie(3, 150, Genere.ACTION)
            movies = movies.stream().filter(movie -> movie.getId().equals(template.getId())).collect(Collectors.toList());
        }

        return movies;
    }

MovieServiceShould.java

    @Before
    public void setUp() throws Exception {
        MovieRepository movieRepository = Mockito.mock(MovieRepository.class);
        movieService = new MovieService(movieRepository);

        Mockito.when(movieRepository.findAll()).thenReturn(
                Arrays.asList(
                        new Movie(1, "Dark Knight", 152, ACTION),
                        new Movie(2, "Memento", 113, THRILLER),
                        new Movie(3, "There's Something About Mary", 119, COMEDY),
                        new Movie(4, "Super 8", 112, THRILLER),
                        new Movie(5, "Scream", 111, HORROR),
                        new Movie(6, "Home Alone", 103, COMEDY),
                        new Movie(7, "Matrix", 136, ACTION)
                )
        );
    }

    @Test
    public void return_movie_by_template() {

        String name = null; // no queremos buscar por nombre
        Integer minutes = 152; // 2h 30m
        Genre genre = Genre.ACTION;
        Movie template = new Movie(name, minutes, genre);
        Collection<Movie> movies =
                movieService.findMoviesByTemplate(template);
        assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(1, 7)));
    }

Prueba:

A proposito, quisiera agrendecer al compañero Antonio Madrid por su aporte :]

MovieService

public class MovieService {

    private final MovieRepository movieRepository;

    public MovieService(MovieRepository movieRepository) {
        this.movieRepository = movieRepository;
    }

    /**
     *  funcion que realiza busquedas en base a los valores
     *  seteados en un objeto de tipo Movie
     * @param template
     * @return
     */
    public Collection<Movie> findMoviesByTemplate(Movie template) {
        Collection<Movie> temporaryResponses = null;
        Collection<Movie> finalMovieCollection = null;

        // Caso especial si todos los valores son nulos
        if (template.equals(new Movie(null,null,null,null))){
            temporaryResponses = Collections.EMPTY_LIST;
        }
        // Si el objeto tiene configurado el id
        if (null != template.getId()){
            finalMovieCollection = movieRepository.findAll().stream().filter(movie -> movie.getId() == template.getId()).collect(Collectors.toList());
        } else {

            /* El resto de casos se ejecutan aqui haciendo uso de la
            funcion orderColection que devuelve una coleccion de datos
            ordenada y sin valores repetidos que sera contenida siempre
            en la coleccion finalMovieCollection*/

            if (null != template.getMinutes()){
                // Si los minutos son menores a 0
                if (0 > template.getMinutes()){
                    throw new IllegalArgumentException("Duration can't be negative");
                }
                temporaryResponses = movieRepository.findAll().stream().filter(movie -> movie.getMinutes() <= template.getMinutes()).collect(Collectors.toList());
                finalMovieCollection = temporaryResponses;
            }
            if (null != template.getName()){
                temporaryResponses = movieRepository.findAll().stream().filter(movie -> movie.getName().toLowerCase().contains(template.getName().toLowerCase())).collect(Collectors.toList());
                finalMovieCollection = orderColection(temporaryResponses,finalMovieCollection);
            }
            if (null != template.getGenre()){
                temporaryResponses = movieRepository.findAll().stream().filter(movie -> movie.getGenre() == template.getGenre()).collect(Collectors.toList());
                finalMovieCollection = orderColection(temporaryResponses,finalMovieCollection);
            }
        }
        return null == finalMovieCollection ? temporaryResponses : finalMovieCollection;
    }

    /**
     * Esta funcion recibe dos colecciones para construir una sola,
     * sin datos repetidos y devuelve una coleccion ordenada por ids
     * @param temporaryResponses
     * @param finalMovieCollection
     * @return
     */
    private Collection<Movie> orderColection(Collection<Movie> temporaryResponses, Collection<Movie> finalMovieCollection){
        Set<Movie> transitive = new HashSet<>();
        for (int i = 0; i < temporaryResponses.size(); i++) {
            transitive.add((Movie) temporaryResponses.toArray()[i]);
        }
        if (null != finalMovieCollection){
            for (int i = 0; i < finalMovieCollection.size(); i++) {
                transitive.add((Movie) finalMovieCollection.toArray()[i]);
            }
        }
        return transitive.stream().sorted(Comparator.comparing(Movie::getId)).collect(Collectors.toList());
    }

}

Test

@RunWith(MockitoJUnitRunner.class)
public class MovieServiceTest {

    @InjectMocks
    private MovieService movieService;

    @Mock
    private MovieRepository movieRepository;


    @Before
    public void setUp() throws Exception {
        when(movieRepository.findAll()).thenReturn(Arrays.asList(
                new Movie(1, "Dark Knight", 152, ACTION),
                new Movie(2, "Memento", 113, THRILLER),
                new Movie(3, "There's Something About Mary", 119, COMEDY),
                new Movie(4, "Super 8", 112, THRILLER),
                new Movie(5, "Scream", 111, HORROR),
                new Movie(6, "Home Alone", 103, COMEDY),
                new Movie(7, "Matrix", 136, ACTION),
                new Movie(8, "Superman", 120, ACTION)
        ));
    }

    @Test
    public void return_movies_by_null_template() {
        Movie template = new Movie(null, null, null, null);
        Collection<Movie> movies = movieService.findMoviesByTemplate(template);
        assertEquals(Collections.EMPTY_LIST, movies);
    }

    @Test(expected = IllegalArgumentException.class)
    public void return_movies_by_negative_minutes_template() {
        Movie template = new Movie(null, null, -1, null);
        Collection<Movie> movies = movieService.findMoviesByTemplate(template);
    }

    @Test
    public void return_movies_by_positive_minutes_template() {
        Movie template = new Movie(null, null, 119, null);
        Collection<Movie> movies = movieService.findMoviesByTemplate(template);
        List<Movie> movieExpected = Arrays.asList(
                new Movie(2, "Memento", 113, THRILLER),
                new Movie(3, "There's Something About Mary", 119, COMEDY),
                new Movie(4, "Super 8", 112, THRILLER),
                new Movie(5, "Scream", 111, HORROR),
                new Movie(6, "Home Alone", 103, COMEDY)
        );
        assertEquals(movieExpected, movies);
    }

    @Test
    public void return_movies_by_name_template() {
        Movie template = new Movie(null, "super", null, null);
        Collection<Movie> movies = movieService.findMoviesByTemplate(template);
        List<Movie> movieExpected = Arrays.asList(
                new Movie(4, "Super 8", 112, THRILLER),
                new Movie(8, "Superman", 120, ACTION)
        );
        assertEquals(movieExpected, movies);
    }

    @Test
    public void return_movies_by_genre_template() {
        Movie template = new Movie(null, null, null, THRILLER);
        Collection<Movie> movies = movieService.findMoviesByTemplate(template);
        List<Movie> movieExpected = Arrays.asList(
                new Movie(2, "Memento", 113, THRILLER),
                new Movie(4, "Super 8", 112, THRILLER)
        );
        assertEquals(movieExpected, movies);
    }

    @Test
    public void return_movies_by_id_template() {
        Movie template = new Movie(1, null, null, null);
        Collection<Movie> movies = movieService.findMoviesByTemplate(template);
        assertEquals(Arrays.asList(1), getMovieIds(movies));
    }

    @Test
    public void return_movies_by_name_and_minutes_template() {
        Movie template = new Movie(null, "Super", 119, null);
        Collection<Movie> movies = movieService.findMoviesByTemplate(template);
        List<Movie> movieExpected = Arrays.asList(
                new Movie(2, "Memento", 113, THRILLER),
                new Movie(3, "There's Something About Mary", 119, COMEDY),
                new Movie(4, "Super 8", 112, THRILLER),
                new Movie(5, "Scream", 111, HORROR),
                new Movie(6, "Home Alone", 103, COMEDY),
                new Movie(8, "Superman", 120, ACTION)
        );
        assertEquals(movieExpected, movies);
    }

    @Test
    public void return_movies_by_name_and_genre_template() {
        Movie template = new Movie(null, "Super", null, ACTION);
        Collection<Movie> movies = movieService.findMoviesByTemplate(template);
        List<Integer> movieExpected = Arrays.asList(1,4,7,8);
        assertEquals(movieExpected, getMovieIds(movies));
    }

    @Test
    public void return_movies_by_minutes_and_genre_template() {
        Movie template = new Movie(null, null, 119, ACTION);
        Collection<Movie> movies = movieService.findMoviesByTemplate(template);
        List<Integer> movieExpected = Arrays.asList(1,2,3,4,5,6,7,8);
        assertEquals(movieExpected, getMovieIds(movies));
    }

    @Test
    public void return_movies_by_name_minutes_and_genre_template() {
        Movie template = new Movie(null, "super", 119, ACTION);
        Collection<Movie> movies = movieService.findMoviesByTemplate(template);
        List<Integer> movieExpected = Arrays.asList(1,2,3,4,5,6,7,8);
        assertEquals(movieExpected, getMovieIds(movies));
    }

    @Test
    public void return_movies_by_id_name_minutes_and_genre_template() {
        Movie template = new Movie(3, "super", 119, ACTION);
        Collection<Movie> movies = movieService.findMoviesByTemplate(template);
        List<Integer> movieExpected = Arrays.asList(3);
        assertEquals(movieExpected, getMovieIds(movies));
    }

    private List<Integer> getMovieIds(Collection<Movie> movies) {
        List<Integer> movieIds = movies.stream().map(Movie::getId).collect(Collectors.toList());
        return movieIds;
    }
}

Mi reto en el siguiente commit

Dejo mi solución

package com.plazti.javatests.movies.model;

import java.util.Objects;

public class Movie {

    private final Integer id;
    private final String name;
    private final Integer minutes;
    private final Genre genre;
    private final String director;

    public Movie(String name, Integer minutes, Genre genre, String director) {
        this(null, name, minutes, genre, director);
    }


    public Movie(Integer id, String name, Integer minutes, Genre genre, String director) {
        this.id = id;
        this.name = name;
        this.minutes = minutes;
        this.genre = genre;
        this.director = director;
    }

    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Integer getMinutes() {
        return minutes;
    }

    public Genre getGenre() {
        return genre;
    }

    public String getDirector() {
        return director;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Movie movie = (Movie) o;
        return Objects.equals(id, movie.id) &&
                Objects.equals(name, movie.name) &&
                Objects.equals(minutes, movie.minutes) &&
                genre == movie.genre &&
                Objects.equals(director, movie.director);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, minutes, genre, director);
    }
}


package com.plazti.javatests.movies.data;

import com.plazti.javatests.movies.model.Genre;
import com.plazti.javatests.movies.model.Movie;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.util.Collection;

public class MovieRepositoryJdbc implements MovieRepository {
    private JdbcTemplate jdbcTemplate;

    public MovieRepositoryJdbc(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public Movie findById(long id) {
        Object[] args = {id};
        return jdbcTemplate.queryForObject("select * from movies where id = ?", args, movieMapper);
    }

    @Override
    public Collection<Movie> findAll() {
        return jdbcTemplate.query("select * from movies", movieMapper);
    }


    @Override
    public void saveOrUpdate(Movie movie) {

        jdbcTemplate.update("insert into movies (name, minutes, genre, director) values(?,?,?,?)",
                movie.getName(), movie.getMinutes(), movie.getGenre().toString(), movie.getDirector());
    }


    private RowMapper<Movie> movieMapper = (rs, rowNum) -> new Movie(
            rs.getInt("id"),
            rs.getString("name"),
            rs.getInt("minutes"),
            Genre.valueOf(rs.getString("genre")),
            rs.getString("director")
    );
}


package com.plazti.javatests.movies.service;

import com.plazti.javatests.movies.data.MovieRepository;
import com.plazti.javatests.movies.model.Genre;
import com.plazti.javatests.movies.model.Movie;

import java.util.Collection;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class MovieService {

    private MovieRepository movieRepository;

    public MovieService(MovieRepository movieRepository) {
        this.movieRepository = movieRepository;
    }

    public Collection<Movie> findMoviesByGenre(Genre genre) {

        return movieRepository.findAll().stream()
                .filter(movie -> movie.getGenre()==genre).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByLength(int length) {
        return movieRepository.findAll().stream()
                .filter(movie -> movie.getMinutes()<=length).collect(Collectors.toList());

    }

    public Collection<Movie> findMoviesByName(String name) {
        return movieRepository.findAll().stream().filter(movie -> movie.getName().toLowerCase().contains(name.toLowerCase())).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByDirector(String director) {
        return movieRepository.findAll().stream().filter(movie -> movie.getDirector().toLowerCase().contains(director.toLowerCase())).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByTemplate(Movie template) {
        if (template.getMinutes()!= null && template.getMinutes()<0){
            throw  new IllegalArgumentException();
        }

        Predicate<Movie> isName = movie ->movie.getName().toLowerCase().contains(Optional.ofNullable(template.getName()).orElse("").toLowerCase());

        Predicate<Movie> isMinutes = movie -> movie.getMinutes() <= Optional.ofNullable(template.getMinutes()).orElse(movie.getMinutes());

        Predicate<Movie> isGenre = movie -> movie.getGenre().toString().contains(Optional.ofNullable(template.getGenre()).orElse(movie.getGenre()).toString());

        Predicate<Movie> isDirector = movie ->  movie.getDirector().toLowerCase().contains(Optional.ofNullable(template.getDirector()).orElse("").toLowerCase());


        return movieRepository.findAll().stream().filter(isName.and(isMinutes).and(isGenre).and(isDirector)).collect(Collectors.toList());
    }

}

package com.plazti.javatests.movies.service;

import com.plazti.javatests.movies.data.MovieRepository;
import com.plazti.javatests.movies.model.Genre;
import com.plazti.javatests.movies.model.Movie;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

public class MovieServiceShould {

    private MovieService movieService;

    @Before
    public void setUp() throws Exception {
        MovieRepository movieRepository = Mockito.mock(MovieRepository.class);
        Mockito.when(movieRepository.findAll()).thenReturn(
                Arrays.asList(
                        new Movie(1, "Dark Knight", 152, Genre.ACTION, "director1"),
                        new Movie(2, "Memento", 113, Genre.THRILLER, "director2"),
                        new Movie(3, "There's Something About Mary", 119, Genre.COMEDY, "director1"),
                        new Movie(4, "Super 8", 112, Genre.THRILLER, "director3"),
                        new Movie(5, "Scream", 111, Genre.HORROR, "director2"),
                        new Movie(6, "Home Alone", 103, Genre.COMEDY, "director1"),
                        new Movie(7, "Matrix", 136, Genre.ACTION, "director3"),
                        new Movie(8, "Superman", 132, Genre.ACTION, "director2")
                )
        );
        movieService = new MovieService(movieRepository);
    }

    @Test
    public void return_movies_by_genre() {

        Collection<Movie> movies = movieService.findMoviesByGenre(Genre.COMEDY);

        assertThat(getMovieIds(movies), is(Arrays.asList(3, 6)));
    }

    @Test
    public void return_movies_by_length() {
        Collection<Movie> movies = movieService.findMoviesByLength(119);
        assertThat(getMovieIds(movies), is(Arrays.asList(2, 3, 4, 5, 6)));

    }


    @Test
    public void return_movies_by_name() {

        Collection<Movie> movies = movieService.findMoviesByName("super");

        assertThat(getMovieIds(movies), is(Arrays.asList(4, 8)));
    }


    @Test
    public void return_movies_by_director() {

        Collection<Movie> movies = movieService.findMoviesByDirector("director1");

        assertThat(getMovieIds(movies), is(Arrays.asList(1, 3, 6)));
    }

    @Test
    public void return_movies_by_template_with_minute_and_genre_and_director() {

        String name = null; //no queremos buscar por nombre
        Integer minutes = 160; // 2h 30m
        Genre genre = Genre.ACTION;
        String director = "director1";
        Movie movie = new Movie(name, minutes, genre, director);

        Collection<Movie> movies = movieService.findMoviesByTemplate(movie);

        assertThat(getMovieIds(movies), is(Arrays.asList(1)));
    }

    @Test
    public void return_movies_by_template_with_name() {

        String name = "super"; //no queremos buscar por nombre
        Integer minutes = null; // 2h 30m
        Genre genre = null;
        String director = null;
        Movie movie = new Movie(name, minutes, genre, director);

        Collection<Movie> movies = movieService.findMoviesByTemplate(movie);

        assertThat(getMovieIds(movies), is(Arrays.asList(4,8)));
    }


    private List<Integer> getMovieIds(Collection<Movie> movies) {
        return movies.stream().map(Movie::getId).collect(Collectors.toList());
    }
}

Comparto mi método de búsqueda de películas por template:

public Collection<Movie> findMoviesByTemplate(Movie template) throws IllegalArgumentException {
		if((template == null) || (template.getMinutes()!=null && template.getMinutes()<0)){
			throw new IllegalArgumentException("Minutes can not be negative");
		}
		
		Stream<Movie> movieStream = movieRepository.findAll().stream();
			
		if(template.getId()!=null) {
			movieStream =  movieStream.filter(movie -> movie.getId() == template.getId());
		}
		else {
			Predicate<Movie> isName = n -> n.getName().toLowerCase().contains(Optional.ofNullable(template.getName()).orElse("").toLowerCase());
			Predicate<Movie> isMinutes = n -> n.getMinutes() <= Optional.ofNullable(template.getMinutes()).orElse(n.getMinutes());
			Predicate<Movie> isGenre = n -> n.getGenre().toString().contains(Optional.ofNullable(template.getGenre()).orElse(Genre.EMPTY).toString());
			movieStream = movieStream.filter(isName.and(isMinutes).and(isGenre));
		}
		return movieStream.collect(Collectors.toList());
		
		
	}
public Collection<Movie> findMoviesByTemplate(Movie template) {

        String templateName = template.getName();
        Genre templateGenre = template.getGenre();
        Integer templateMinutes = template.getMinutes();
        Integer templateId = template.getId();

        if(templateMinutes != null && templateMinutes < 0){
            throw new IllegalArgumentException("Duration negative.");
        }

        Collection<Movie> moviesFromDb = new ArrayList<>();

        if(templateId != null){
            Movie movie = findById(templateId);
            moviesFromDb.add(movie);
            return moviesFromDb;
        }

        moviesFromDb = findAll();
        if(templateName != null){
            moviesFromDb = moviesFromDb.stream().filter(movie -> movie.getName().toLowerCase()
                    .contains(templateName.toLowerCase()))
                    .collect(Collectors.toList());
        }
        if(templateGenre != null){
            moviesFromDb = moviesFromDb.stream().filter(movie -> movie.getGenre() == templateGenre)
                    .collect(Collectors.toList());
        }
        if(templateMinutes != null){

            moviesFromDb = moviesFromDb.stream().filter(movie -> movie.getMinutes() <= templateMinutes)
                    .collect(Collectors.toList());
        }

        return moviesFromDb;
    }```

Comparto la implementación del ejercicio.

Movie.java

import java.util.Objects;

public class Movie
{
    private Integer id;
    private String name;
    private Integer minutes;
    private Genre genre;
    private String director;


    public Movie(String name, Integer minutes, Genre genre, String director)
    {
        this(null, name, minutes, genre, director);
    }


    public  Movie(Integer id, String name, Integer minutes, Genre genre, String director)
    {
        this.id = id;
        this.name = name;
        this.minutes = minutes;
        this.genre = genre;
        this.director = director;
    }


    public Integer getId()
    {
        return id;
    }

    public String getName()
    {
        return name;
    }

    public Integer getMinutes()
    {
        return minutes;
    }

    public Genre getGenre()
    {
        return genre;
    }

    public String getDirector()
    {
        return director;
    }

    
    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Movie movie = (Movie) o;
        return minutes == movie.minutes &&
                Objects.equals(id, movie.id) &&
                Objects.equals(name, movie.name) &&
                Objects.equals(director, movie.director) &&
                genre == movie.genre;
    }

    @Override
    public int hashCode()
    {
        return Objects.hash(id, name, minutes, genre, director);
    }
}

MovieService.java

import leidy.javatests.movies.data.MovieRepository;
import leidy.javatests.movies.model.Genre;
import leidy.javatests.movies.model.Movie;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;


public class MovieService
{
    private  MovieRepository repository;


    public MovieService(MovieRepository repository)
    {
        this.repository = repository;
    }



    public Collection<Movie> findMoviesByGenre(Genre genre)
    {
        Collection<Movie> allMovies = repository.findAll().stream()
                .filter(movie -> movie.getGenre() == genre).collect(Collectors.toList());
        return allMovies;
    }


    public Collection<Movie> findMoviesByDuration(Integer minutes)
    {
        Collection<Movie> allTimes = repository.findAll().stream()
                .filter(movie -> movie.getMinutes() == minutes).collect(Collectors.toList());
        return allTimes;
    }


    public Collection<Movie> findMovieByName(String name)
    {
        String word = convertUpperCase(name);

        List<Movie> allNames = repository.findAll().stream()
                .filter(movie -> movie.getName().contains(word)).collect(Collectors.toList());
        return allNames;
    }


    public Collection<Movie> findMovieById(Integer id)
    {
        List<Movie> allIds = repository.findAll().stream()
                .filter(movie -> movie.getId() == id).collect(Collectors.toList());
        return allIds;
    }



    public Collection<Movie> findMovieByDirector(String director)
    {
        String word = convertUpperCase(director);

        List<Movie> allDirectors = repository.findAll().stream()
                .filter(movie -> movie.getDirector().contains(word)).collect(Collectors.toList());
        return allDirectors;
    }


    public static String convertUpperCase(String str)
    {
        String result = "";

        for (int i  = 0; i < str.length(); i++)
        {
            Character letterOne = str.charAt(0);
            Character c = str.charAt(i);

            if (i == 0 )
            {
                c = Character.toUpperCase(c);
            }
            result += c;
        }
        return result;
    }



    public Collection<Movie> findMoviesByTemplate(Movie template)
    {
        Collection<Movie> returnMovies = repository.findAll();

        if ( template.getId() != null)
        {
            returnMovies = findMovieById(template.getId());
        }


        if (template.getMinutes() < 0)
            throw new IllegalArgumentException("This value is not allowed");

         else
        {
            returnMovies = returnMovies.stream().filter(movie -> movie.getMinutes() > template.getMinutes()).collect(Collectors.toList());
        }


        if (template.getName() != null)
        {
            returnMovies = returnMovies.stream().filter(movie -> movie.getName()
                    .toLowerCase().contains(template.getName().toLowerCase())).collect(Collectors.toList());
        }


        if(template.getGenre() != null)
        {
            returnMovies = returnMovies.stream().filter(movie -> movie.getGenre().equals(template.getGenre())).collect(Collectors.toList());
        }


        if (template.getDirector() != null)
        {
            returnMovies = returnMovies.stream().filter(movie -> movie.getDirector()
                    .toLowerCase().contains(template.getDirector().toLowerCase())).collect(Collectors.toList());
        }
        return returnMovies;
    }
}

MovieServiceShould.java

import leidy.javatests.movies.data.MovieRepository;
import leidy.javatests.movies.model.Genre;
import leidy.javatests.movies.model.Movie;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

public class MovieServiceShould
{
    private MovieService movieService;

    @Before
    public void setup()
    {
        MovieRepository repository = Mockito.mock(MovieRepository.class);
        Mockito.when(repository.findAll()).thenReturn(
                Arrays.asList(
                        new Movie(1, "Dark Knight", 152, Genre.ACTION, "Christopher Nolan"),
                        new Movie(2, "Memento", 113, Genre.THRILLER,"Christopher Nolan"),
                        new Movie(3, "Kill Bill", 105, Genre.ACTION, "Quentin Tarantino"),
                        new Movie(4, "Scream", 111, Genre.HORROR, "Wes Craven"),
                        new Movie(5, "Home Alone", 103, Genre.COMEDY, "Chris Columbus"),
                        new Movie(6, "Evil Dead", 120, Genre.HORROR, "Sam Raimi"),
                        new Movie(7, "Super 8", 130, Genre.THRILLER, "Jeffrey Jacob Abrams"),
                        new Movie(8, "Matrix", 136, Genre.ACTION, "Lilly Wachowski"),
                        new Movie(9, "Superman", 150, Genre.ACTION, "Richard Donner"),
                        new Movie(10, "Jackie Brown", 110, Genre.ACTION, "Quentin Tarantino"),
                        new Movie(11, "Resident Evil", 145, Genre.ACTION, "Paul William Scott Anderson"),
                        new Movie(12, "Mortal Kombat", 130, Genre.ACTION, "Paul William Scott Anderson")
                )
        );

        movieService = new MovieService(repository);
    }


    @Test
    public void return_movies_by_genre()
    {
       Collection<Movie> movies = movieService.findMoviesByGenre(Genre.HORROR);
        List<String> movieName = movies.stream().map(movie -> movie.getName()).collect(Collectors.toList());

        assertThat(movieName, is(Arrays.asList("Scream", "Evil Dead")));
    }


    @Test
    public void return_movies_by_duration()
    {
        Collection<Movie> movies = movieService.findMoviesByDuration(105);
        List<String> moviesDuration = movies.stream().map(Movie::getName).collect(Collectors.toList());

        // return movie name
        assertThat(moviesDuration, is(Arrays.asList("Kill Bill")));

        // return movie id
        assertThat(getMovieIds(movies), is(Arrays.asList(3)));
    }


    @Test
    public void return_movie_by_name_or_keyword()
    {
        Collection<Movie> movies = movieService.findMovieByName("super");
        List<String> movieName = movies.stream().map(Movie::getName).collect(Collectors.toList());

        assertThat(movieName, is(Arrays.asList("Super 8", "Superman")));
    }

    @Test
    public void return_movies_by_director()
    {
        Collection<Movie> movies = movieService.findMovieByDirector("scot");
        List<String> movieDirector = movies.stream().map(movie -> movie.getName()).collect(Collectors.toList());

        assertThat(movieDirector, is(Arrays.asList("Resident Evil", "Mortal Kombat")));
    }


    @Test
    public void return_movies_of_action_with_director_and_more_than_100_minutes()
    {
        Collection<Movie> movies = movieService.findMoviesByTemplate
                (new Movie(null, 100, Genre.ACTION, "quentin"));

        List<String> movie = movies.stream().map(Movie::getName).collect(Collectors.toList());

        assertThat(movie, is(Arrays.asList("Kill Bill", "Jackie Brown")));
    }


    @Test
    public void return_ids_movies_by_name_and_more_than_120_minutes()
    {
        Collection<Movie> movies = movieService.
                findMoviesByTemplate(new Movie("super", 120, null, null));

        assertThat(getMovieIds(movies), is(Arrays.asList(7, 9)));
    }

    @Test
    public void return_movie_by_director_and_time()
    {
        Collection<Movie> movies = movieService.findMoviesByTemplate
                (new Movie(null, 90, null, "lilly"));

        List<String> name = movies.stream().map(movie -> movie.getName()).collect(Collectors.toList());

        assertThat(name, is(Arrays.asList("Matrix")));
    }


    @Test (expected = IllegalArgumentException.class)
    public void return_error_when_minutes_are_negative()
    {
        Collection<Movie> movies = movieService.findMoviesByTemplate(
                new Movie(null, -120, Genre.HORROR, null));
        assertThat(movies, is(Arrays.asList()));
    }


    private List<Integer> getMovieIds(Collection<Movie> movies)
    {
        List<Integer> getId = movies.stream().map(Movie::getId).collect(Collectors.toList());
        return getId;
    }
}

Hola, aquí les comparto mi versión del código para búsqueda por template

public Collection<Movie> findMoviesByTemplate(Movie template){

        Predicate<Movie> finalPredicate= null;

        if(template.getName()!=null)
        {
            Predicate<Movie> namePredicate =   movie -> movie.getName().contains(template.getName());
            finalPredicate = finalPredicate==null?namePredicate:finalPredicate.and(namePredicate);
        }
        if(template.getGenre()!=null)
        {
            Predicate<Movie> genrePredicate =  movie -> movie.getGenre() == template.getGenre();
            finalPredicate = finalPredicate==null?genrePredicate:finalPredicate.and(genrePredicate);
        }
        if(template.getMinutes()!=null)
        {
            Predicate<Movie> minutePredicate = movie -> movie.getMinutes() <= template.getMinutes();
            finalPredicate = finalPredicate==null?minutePredicate:finalPredicate.and(minutePredicate);
        }
        if(template.getDirector()!=null)
        {
            Predicate<Movie> directorPredicate = movie -> movie.getDirector().contains(template.getDirector());
            finalPredicate = finalPredicate==null?directorPredicate:finalPredicate.and(directorPredicate);
        }

        if(finalPredicate==null)
            return new ArrayList<>();

        return  movieRepository.findAll().stream().filter(finalPredicate).collect(Collectors.toList());

    }

public Collection<Movie> findMoviesByTemplate(Movie template){

    Predicate<Movie> finalPredicate= null;

    if(template.getName()!=null)
    {
        Predicate<Movie> namePredicate =   movie -> movie.getName().contains(template.getName());
        finalPredicate = finalPredicate==null?namePredicate:finalPredicate.and(namePredicate);
    }
    if(template.getGenre()!=null)
    {
        Predicate<Movie> genrePredicate =  movie -> movie.getGenre() == template.getGenre();
        finalPredicate = finalPredicate==null?genrePredicate:finalPredicate.and(genrePredicate);
    }
    if(template.getMinutes()!=null)
    {
        Predicate<Movie> minutePredicate = movie -> movie.getMinutes() <= template.getMinutes();
        finalPredicate = finalPredicate==null?minutePredicate:finalPredicate.and(minutePredicate);
    }
    if(template.getDirector()!=null)
    {
        Predicate<Movie> directorPredicate = movie -> movie.getDirector().contains(template.getDirector());
        finalPredicate = finalPredicate==null?directorPredicate:finalPredicate.and(directorPredicate);
    }

    if(finalPredicate==null)
        returnnew ArrayList<>();

    return  movieRepository.findAll().stream().filter(finalPredicate).collect(Collectors.toList());

}
public Collection<Movie> findMoviesByTemplate(Movie template) {
        if(template.getMinutes() != null && template.getMinutes() < 0)throw new IllegalArgumentException();
        return movieRepository.findAll().stream().filter(
                movie ->
                        movie.getName() == template.getName() ||
                                movie.getGenre() == template.getGenre() ||
                                movie.getMinutes() == template.getMinutes()||
                                movie.getId() == template.getId()
        ).collect(Collectors.toList());
    }

ok

Reto Listo 😃

Datos

Tests

MoviService Method findMoviesByTemplate

Test Run

Buen ejercicio instructor Ferran, acá dejo mis respuestas:
    

    

MovieService.java

public Collection<Movie> findByTemplate(Movie template){

        if (template.equals(new Movie(null,null,null))){
            return Collections.emptyList();
        }

        return movieRepository
                .findAll().stream().filter(movie -> this.filterTemplate(movie, template)).collect(Collectors.toList());
    }

    private boolean filterTemplate(Movie movie, Movie template){
        boolean result = true;

        if (template.getId() != null){
            return movie.getId().equals(template.getId());
        }

        if (template.getName() != null) {
            result = result && movie.getName().toLowerCase().contains(template.getName().toLowerCase());
        }
        if (template.getMinutes() != null) {
            result = result && movie.getMinutes() <= template.getMinutes();
        }
        if (template.getMinutes() != null) {
            if (template.getMinutes() < 0){
                throw new IllegalArgumentException("Los minutos no pueden ser negativos en la búsqueda");
            }
            result = result && movie.getGenre().equals(template.getGenre());
        }
        if (template.getDirector() != null) {
            result = result && movie.getDirector().toLowerCase().contains(template.getDirector().toLowerCase());
        }

        return result;
    }

MovieServiceShould.java

@Test
    public void return_movies_by_template() {
        Movie template = new Movie(null, 150, Genre.ACTION);

        Collection<Movie> movies = movieService.findByTemplate(template);

        assertThat(getIds(movies), CoreMatchers.is(Arrays.asList(7,8)));
    }

    @Test
    public void return_movies_by_template_all_null() {
        Movie template = new Movie(null, null, null);

        Collection<Movie> movies = movieService.findByTemplate(template);

        assertThat(getIds(movies), CoreMatchers.is(Arrays.asList()));
    }

    @Test
    public void return_movies_by_template_with_id() {
        Movie template = new Movie(1,"Super", null, null);  //debe ignorar el criterio de nombre

        Collection<Movie> movies = movieService.findByTemplate(template);

        assertThat(getIds(movies), CoreMatchers.is(Arrays.asList(1)));
    }

    @Test(expected = IllegalArgumentException.class)
    public void return_movies_by_template_illegal_argument_exception() {
        Movie template = new Movie("Super", -1, null);  //debe ignorar el criterio de nombre

        Collection<Movie> movies = movieService.findByTemplate(template);
    }

Método findMoviesByTemplate

public Collection<Movie> findMoviesByTemplate(Movie template) {
    return movieRepository.findAll().stream()
            .filter(movie -> {
                return
            template.getName() !=null? movie.getName().toLowerCase().contains( template.getName().toLowerCase() ): movie.getName() != null &&
            template.getMinutes() != null? movie.getMinutes() <= template.getMinutes(): movie.getMinutes() > 0 &&
            template.getGenre() != null ? movie.getGenre().equals(template.getGenre() ): movie.getGenre() != null &&
            template.getDirector() != null ? movie.getDirector().toLowerCase().contains( template.getDirector().toLowerCase() ) : movie.getDirector() != null;
            }).collect(Collectors.toList());
}

Test

@Test
public void return_movies_by_template() {
    Collection<Movie> movies = movieService.findMoviesByTemplate( new Movie("super",112, null, null));
    assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(4)) );
}

Que tal comparto mis resultados.
MovieService.class

public class MovieService{

    private MovieRepository movieRepository;

    public MovieService(MovieRepository movieRepository) {
        this.movieRepository = movieRepository;
    }
    public Movie findMovieById(int id) {
        return movieRepository.findAll().stream()
                .filter(movie -> movie.getId() == id).collect(Collectors.toList()).get(0);
    }
    public Collection<Movie> findMoviesByGenre(Genre genre) {
        return movieRepository.findAll().stream()
                .filter(movie -> movie.getGenre() == genre).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByLength(int length) {
        return movieRepository.findAll().stream()
                .filter(movie -> movie.getMinutes() <= length).collect(Collectors.toList());
    }
    public Collection<Movie> findByName(String name){
        ArrayList<Movie> movies = new ArrayList<>();
        String[] tokens = name.trim().split(" ");
        for (String n : tokens) {
            movies.addAll(movieRepository.findAll().stream()
                    .filter(movie -> movie.getName().trim().toLowerCase().contains(n.trim().toLowerCase()))
                    .collect(Collectors.toList()));
        }
        return deleteMoviesDuplicatedAndOrderByIdAsc(movies,movies);
    }
    public Collection<Movie> findByDirector(String director){
        String[] tokens = director.trim().split(" ");
        ArrayList<Movie> movies = new ArrayList<>();
        for (String n : tokens) {
            movies.addAll(movieRepository.findAll().stream()
                    .filter(movie -> movie.getDirector().toLowerCase().contains(director.toLowerCase()))
                    .collect(Collectors.toList()));
        }
        return deleteMoviesDuplicatedAndOrderByIdAsc(movies,movies);
    }

    public Collection<Movie> findMoviesByTemplate(Movie template) {
        Collection<Movie> movies = new ArrayList<>();
        if(template.getId() != null){
            movies.add(findMovieById(template.getId()));
        }else{
            if(template.getMinutes() != null){
                movies.addAll(findMoviesByLength(template.getMinutes()));
            }
            if(template.getGenre() != null){
                movies.addAll(findMoviesByGenre(template.getGenre()));
            }
            if(template.getDirector() != null){
                movies.addAll(findByDirector(template.getDirector()));
            }
            if(template.getName() != null){
                movies.addAll(findByName(template.getName()));
            }
        }
        return deleteMoviesDuplicatedAndOrderByIdAsc(movies,movies);
    }
    public static Collection<Movie> deleteMoviesDuplicatedAndOrderByIdAsc(Collection<Movie> list1,Collection<Movie> list2){
        ArrayList<Movie> movies = (ArrayList<Movie>) Stream.concat(list1.stream(),list2.stream())
                .distinct()
                .collect(Collectors.toList());
        movies.sort(new SortById());
        return movies;
    }
}

public class MovieServiceTest {
    private MovieService movieService;

    @Before
    public void setUp() throws Exception {
        MovieRepository movieRepository = Mockito.mock(MovieRepository.class);
        Mockito.when(movieRepository.findAll()).thenReturn(
                Arrays.asList(
                        new Movie(1, "Dark Knight", 152, Genre.ACTION,"Christopher Nolan"),
                        new Movie(2, "Memento", 113, Genre.THRILLER,"Christopher Nolan"),
                        new Movie(3, "There's Something About Mary", 119, Genre.COMEDY,"Bobby Farrelly"),
                        new Movie(4, "Super 8", 112, Genre.THRILLER,"Steven Spielberg"),
                        new Movie(5, "Scream", 111, Genre.HORROR,"Jill Blotevogel"),
                        new Movie(6, "Home Alone", 103, Genre.COMEDY,"Chris Columbus"),
                        new Movie(7, "Matrix", 136, Genre.ACTION,"Lilly Wachowski"),
                        new Movie(8, "Scream return", 111, Genre.HORROR,"Bobby Farrelly")
                )
        );
        movieService = new MovieService(movieRepository);
    }
    @Test
    public void return_movies_by_genre() {
        Collection<Movie> movies = movieService.findMoviesByGenre(Genre.COMEDY);
        assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(3, 6)) );
    }
    @Test
    public void return_movies_by_length() {
        Collection<Movie> movies = movieService.findMoviesByLength(119);
        assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(2, 3, 4, 5, 6, 8)) );
    }
    @Test
    public void return_movies_by_name() {
        Collection<Movie> movies = movieService.findByName("return Scream");
        Collection<Movie> movies2 = movieService.findByName("Knight Dark");
        assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(5, 8)) );
        assertThat(getMovieIds(movies2), CoreMatchers.is(Arrays.asList(1)) );
    }
    @Test
    public void return_movies_by_director() {
        Collection<Movie> movies = movieService.findByDirector("nolan");
        assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(1, 2)) );
    }
    @Test
    public void return_movies_template_by_id() {
        Collection<Movie> movies = movieService.findMoviesByTemplate(new Movie(2,"Dark Knight",111,Genre.THRILLER,null));
        assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(2)));
    }
    @Test
    public void return_movies_template_by_name_and_minutes() {
        Collection<Movie> movies = movieService.findMoviesByTemplate(new Movie("Dark Knight",111,null,null));
        assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(1,5,6,8)));
    }

    @Test
    public void return_movies_by_name_and_minutes_and_genre() {
        Collection<Movie> movies = movieService.findMoviesByTemplate(new Movie("Dark Knight",111,Genre.THRILLER,null));
        assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(1,2,4,5,6,8)));
    }

    @Test
    public void return_movies_by_name_and_minutes_and_genre_director() {
        Collection<Movie> movies = movieService.findMoviesByTemplate(new Movie("Matrix",112,Genre.ACTION,"Blotevogel"));
        assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(1,4,5,6,7,8)));
    }

    @Test
    public void return_movies_by_minutes_genre() {
        Collection<Movie> movies = movieService.findMoviesByTemplate(new Movie(null,180,Genre.THRILLER,null));
        assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(1,2,3,4,5,6,7,8)));
    }

    @Test
    public void return_movies_by_minutes_genre_director() {
        Collection<Movie> movies = movieService.findMoviesByTemplate(new Movie(null,103,Genre.HORROR,"lly"));
        assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(3,5,6,7,8)));
    }

    @Test
    public void return_movies_by_template_by_director() {
        Collection<Movie> movies = movieService.findMoviesByTemplate(new Movie(null,null,null,null,"Christopher Nolan"));
        assertThat(getMovieIds(movies), CoreMatchers.is(Arrays.asList(1,2)));
    }
    private List<Integer> getMovieIds(Collection<Movie> movies) {
        return movies.stream().map(Movie::getId).collect(Collectors.toList());
    }
}

public class SortById implements Comparator<Movie> {
    public int compare(Movie a, Movie b){
        return a.getId() - b.getId();
    }
}
public interface MovieFilterChainResponsability {

    Collection <Movie> apply(Movie movie,Collection <Movie> movies);
}
public class FilterByDuration implements MovieFilterChainResponsability{

    private MovieFilterChainResponsability movieFilterChainResponsability;

    public FilterByDuration(MovieFilterChainResponsability movieFilterChainResponsability) {
        this.movieFilterChainResponsability = movieFilterChainResponsability;
    }

    public FilterByDuration() {
    }

    @Override
    public Collection<Movie> apply(Movie movie, Collection<Movie> movies) {
        Collection<Movie> temp = movies;
        if(movie.getMinutes()!=null) {
            if(movie.getMinutes() < 0)
                throw new IllegalArgumentException("Duration can't be negative");
            temp = movies.stream().filter(mov -> mov.getMinutes() <= movie.getMinutes()).collect(Collectors.toList());
        }
        return movieFilterChainResponsability == null ? temp : movieFilterChainResponsability.apply(movie,temp);
    }
}
public class FilterByGenre implements MovieFilterChainResponsability{

    private MovieFilterChainResponsability movieFilterChainResponsability;

    public FilterByGenre(MovieFilterChainResponsability movieFilterChainResponsability) {
        this.movieFilterChainResponsability = movieFilterChainResponsability;
    }

    public FilterByGenre() {
    }

    @Override
    public Collection<Movie> apply(Movie movie, Collection<Movie> movies) {
        Collection<Movie> temp = movies;
        if(movie.getGenre()!=null)
            temp = movies.stream().filter(mov -> mov.getGenre() == movie.getGenre()).collect(Collectors.toList());
        return movieFilterChainResponsability == null ? temp : movieFilterChainResponsability.apply(movie,temp);
    }
}
public class FilterById implements MovieFilterChainResponsability{


    private MovieFilterChainResponsability movieFilterChainResponsability;

    public FilterById(MovieFilterChainResponsability movieFilterChainResponsability) {
        this.movieFilterChainResponsability = movieFilterChainResponsability;
    }

    public FilterById() {
    }

    @Override
    public Collection<Movie> apply(Movie movie,Collection <Movie> movieList) {
        if(movie.getId()!=null)
            return movieList.stream().filter(mov -> mov.getId() == movie.getId()).collect(Collectors.toList());
        return movieFilterChainResponsability == null ? movieList : movieFilterChainResponsability.apply(movie,movieList);
    }
}
public class FilterByName implements MovieFilterChainResponsability{

    private MovieFilterChainResponsability movieFilterChainResponsability;

    public FilterByName() {}

    public FilterByName(MovieFilterChainResponsability movieFilterChainResponsability) {
        this.movieFilterChainResponsability = movieFilterChainResponsability;
    }


    @Override
    public Collection<Movie> apply(Movie movie, Collection<Movie> movies) {
        Collection<Movie> temp = movies;
        if(movie.getName()!=null)
            temp = movies.stream().filter(mov -> mov.getName().toLowerCase().contains(movie.getName().toLowerCase())).collect(Collectors.toList());
        return movieFilterChainResponsability == null ? temp : movieFilterChainResponsability.apply(movie,temp);
    }
}
public class FilterByNameDirector implements MovieFilterChainResponsability{

    private MovieFilterChainResponsability movieFilterChainResponsability;

    public FilterByNameDirector(MovieFilterChainResponsability movieFilterChainResponsability) {
        this.movieFilterChainResponsability = movieFilterChainResponsability;
    }

    public FilterByNameDirector() {
    }

    @Override
    public Collection <Movie> apply(Movie movie, Collection <Movie> movieList) {
        Collection <Movie> temp = movieList;
        if(movie.getDirector()!=null)
            temp = movieList.stream().filter(mov -> mov.getDirector().toLowerCase().contains(movie.getDirector().toLowerCase())).collect(Collectors.toList());
        return movieFilterChainResponsability == null ? temp : movieFilterChainResponsability.apply(movie,temp);
    }
}
public class MovieService {

    MovieRepository movieRepository;

    public MovieService(MovieRepository movieRepository) {
        this.movieRepository = movieRepository;
    }

    public Collection <Movie> findMoviesByGenre(Genre genre) {
        Collection <Movie> all = movieRepository.findAll();
        Movie template = new Movie(null, null, null, genre, null);
        return new FilterByGenre().apply(template,all);
    }

    public Collection<Movie> findMoviesByLenght(int duration) {
        Collection <Movie> all = movieRepository.findAll();
        Movie template = new Movie(null, null, duration, null, null);
        return new FilterByDuration().apply(template,all);
    }

    public Collection<Movie> findMoviesByName(String name) {
        Collection <Movie> all = movieRepository.findAll();
        Movie template = new Movie(null, name, null, null, null);
        return new FilterByName().apply(template,all);
    }

    public Collection<Movie> findMoviesByDirector(String directorName) {
        Collection <Movie> all = movieRepository.findAll();
        Movie template = new Movie(null, null, null, null, directorName);
        return new FilterByNameDirector().apply(template,all);
    }

    public Collection<Movie> findMoviesByTemplate(Movie movie) {
        Collection <Movie> all = movieRepository.findAll();
        MovieFilterChainResponsability filterByDuration = new FilterByDuration();
        MovieFilterChainResponsability filterByGenre = new FilterByGenre(filterByDuration);
        MovieFilterChainResponsability filterByName = new FilterByName(filterByGenre);
        MovieFilterChainResponsability filterByDirector = new FilterByNameDirector(filterByName);
        MovieFilterChainResponsability filterById = new FilterById(filterByDirector);
        return filterById.apply(movie,all);
    }
}
public class MovieServiceTest {

    private MovieRepository movieRepository;
    private MovieService movieService;

    @Before
    public void setUp() {
        movieRepository = Mockito.mock(MovieRepository.class);
        when(movieRepository.findAll()).thenReturn(Arrays.asList(
                new Movie(1,"Dark Knight",152,Genre.ACTION,"director2"),
                new Movie(2,"Memento",113,Genre.THRILLER,"director1"),
                new Movie(3,"There's Something About Mary",119,Genre.COMEDY,"director2"),
                new Movie(4,"Super 8",112,Genre.THRILLER,"director1"),
                new Movie(5,"Scream",111,Genre.HORROR,"director2"),
                new Movie(6,"Home Alone",103,Genre.COMEDY,"director1"),
                new Movie(7,"Matrix",136,Genre.ACTION,"director2"),
                new Movie(8,"Superman",140,Genre.ACTION,"director2")
        ));
        movieService = new MovieService(movieRepository);
    }

    @Test
    public void return_movies_by_genre_then_return_3_6() {
        Collection<Movie> moviesByGenre = movieService.findMoviesByGenre(Genre.COMEDY);
        List<Integer> collect = moviesByGenre.stream().map(Movie::getId).collect(Collectors.toList());
        assertThat(collect, CoreMatchers.is(Arrays.asList(3,6)));
    }

    @Test
    public void return_movies_by_duration_then_return_2_3_4_5_6() {
        Collection<Movie> moviesByGenre = movieService.findMoviesByLenght(120);
        List<Integer> collect = moviesByGenre.stream().map(Movie::getId).collect(Collectors.toList());
        assertThat(collect, CoreMatchers.is(Arrays.asList(2,3,4,5,6)));
    }

    @Test
    public void return_movies_by_name_then_return_4_8(){
        Collection<Movie> moviesByName = movieService.findMoviesByName("super");
        List<Integer> collect = moviesByName.stream().map(Movie::getId).collect(Collectors.toList());
        assertThat(collect, CoreMatchers.is(Arrays.asList(4,8)));
    }

    @Test
    public void return_movies_by_director_then_return_1_3_5_7_8(){
        Collection<Movie> moviesByName = movieService.findMoviesByDirector("Director2");
        List<Integer> collect = moviesByName.stream().map(Movie::getId).collect(Collectors.toList());
        assertThat(collect, CoreMatchers.is(Arrays.asList(1,3,5,7,8)));
    }

    @Test
    public void given_template_with_id_and_all_other_params_it_will_return_only_the_id_movie(){
        Movie movie = new Movie(4, "Memento", 123, Genre.THRILLER, "director1");
        Collection <Movie> moviesByTemplate = movieService.findMoviesByTemplate(movie);
        List<Integer> collect = moviesByTemplate.stream().map(Movie::getId).collect(Collectors.toList());
        assertThat(collect,CoreMatchers.is(Arrays.asList(4)));
    }

    @Test
    public void given_template_without_id_and_with_director_name_params_it_will_return_movies_2_4_6(){
        Movie movie = new Movie(null, null, null, null, "director1");
        Collection <Movie> moviesByTemplate = movieService.findMoviesByTemplate(movie);
        List<Integer> collect = moviesByTemplate.stream().map(Movie::getId).collect(Collectors.toList());
        assertThat(collect,CoreMatchers.is(Arrays.asList(2,4,6)));
    }

    @Test
    public void given_template_with_duration_and_genre_ACTION_then_return_movies_7_8(){
        Movie movie = new Movie(null, null, 140, Genre.ACTION, null);
        Collection <Movie> moviesByTemplate = movieService.findMoviesByTemplate(movie);
        List<Integer> collect = moviesByTemplate.stream().map(Movie::getId).collect(Collectors.toList());
        assertThat(collect,CoreMatchers.is(Arrays.asList(7,8)));
    }

    @Test(expected = IllegalArgumentException.class)
    public void given_template_with_negative_duration_it_will_throw_and_IllegalArgumentException(){
        Movie movie = new Movie(null, null, -120, Genre.ACTION, null);
        Collection <Movie> moviesByTemplate = movieService.findMoviesByTemplate(movie);
        List<Integer> collect = moviesByTemplate.stream().map(Movie::getId).collect(Collectors.toList());
        assertThat(collect,CoreMatchers.is(Arrays.asList(7,8)));
    }
}

MovieService.java

package com.mael.javatests.movies.service;

import com.mael.javatests.movies.data.MovieRepository;
import com.mael.javatests.movies.model.Genre;
import com.mael.javatests.movies.model.Movie;

import java.util.Collection;
import java.util.Collections;
import java.util.stream.Collectors;

public class MovieService {

    private MovieRepository movieRepository;

    public MovieService(MovieRepository movieRepository) {
        this.movieRepository = movieRepository;
    }

    public Collection<Movie> findMoviesByGenre(Genre genero) {
        return movieRepository.findAll().stream()
                .filter(movie -> movie.getGenre() == genero).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByDuration(int minutes) {
        return movieRepository.findAll().stream()
                .filter(movie -> movie.getMinutes() <= minutes).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByDirector(String director) {
        return movieRepository.findByDirector(director);
    }
    
    public Collection<Movie> findMoviesByTemplate(Movie template) {
        if (template.equals(new Movie(null, null, null, null, null)))
            return Collections.emptyList();

        return movieRepository.findAll().stream().filter(movie -> filterTemplate(movie, template))
                .collect(Collectors.toList());

    }

    private boolean filterTemplate(Movie movie, Movie template){
        boolean result = true;
        if (template.getId() != null){
            return movie.getId().equals(template.getId());
        }

        if (template.getName() != null){
            result = result && movie.getName().toLowerCase().contains(template.getName().toLowerCase());
        }

        if (template.getMinutes() != null){
            if (template.getMinutes() < 0) throw new IllegalArgumentException("No se admiten numeros negativos");

            result = result && movie.getMinutes() <= template.getMinutes();
        }

        if (template.getGenre() != null){
            result = result && movie.getGenre() == template.getGenre();
        }

        if (template.getDirector() != null){
            result = result && movie.getDirector().equals(template.getDirector());
        }

        return result;
    }
}

MovieServiceTest.java

package com.mael.javatests.movies.service;

import com.mael.javatests.movies.data.MovieRepository;
import com.mael.javatests.movies.model.Genre;
import com.mael.javatests.movies.model.Movie;

import java.util.Collection;
import java.util.Collections;
import java.util.stream.Collectors;

public class MovieService {

    private MovieRepository movieRepository;

    public MovieService(MovieRepository movieRepository) {
        this.movieRepository = movieRepository;
    }

    public Collection<Movie> findMoviesByGenre(Genre genero) {
        return movieRepository.findAll().stream()
                .filter(movie -> movie.getGenre() == genero).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByDuration(int minutes) {
        return movieRepository.findAll().stream()
                .filter(movie -> movie.getMinutes() <= minutes).collect(Collectors.toList());
    }

    public Collection<Movie> findMoviesByDirector(String director) {
        return movieRepository.findByDirector(director);
    }
    
    public Collection<Movie> findMoviesByTemplate(Movie template) {
        if (template.equals(new Movie(null, null, null, null, null)))
            return Collections.emptyList();

        return movieRepository.findAll().stream().filter(movie -> filterTemplate(movie, template))
                .collect(Collectors.toList());

    }

    private boolean filterTemplate(Movie movie, Movie template){
        boolean result = true;
        if (template.getId() != null){
            return movie.getId().equals(template.getId());
        }

        if (template.getName() != null){
            result = result && movie.getName().toLowerCase().contains(template.getName().toLowerCase());
        }

        if (template.getMinutes() != null){
            if (template.getMinutes() < 0) throw new IllegalArgumentException("No se admiten numeros negativos");

            result = result && movie.getMinutes() <= template.getMinutes();
        }

        if (template.getGenre() != null){
            result = result && movie.getGenre() == template.getGenre();
        }

        if (template.getDirector() != null){
            result = result && movie.getDirector().equals(template.getDirector());
        }

        return result;
    }
}

Código de función findMoviesByTemplate, para validar varias condiciones a la vez.

return movieRepository.findAll().stream()
                .filter(movie -> template.getName() !=null? movie.getName().toLowerCase().contains( template.getName().toLowerCase() ): movie.getName() != null)
                .filter(movie -> template.getMinutes() != null? movie.getMinutes() <= template.getMinutes(): movie.getMinutes() > 0)
                .filter(movie -> template.getGenre() != null ? movie.getGenre().equals(template.getGenre() ): movie.getGenre() != null)
                .filter(movie -> template.getDirector() != null ? movie.getDirector().toLowerCase().contains( template.getDirector().toLowerCase()) : movie.getDirector() != null)
                .collect(Collectors.toList());