Skip to content

Latest commit

 

History

History
436 lines (294 loc) · 11.2 KB

02_MovieLibrary-class.md

File metadata and controls

436 lines (294 loc) · 11.2 KB

Testing the Movie Rating CLI app

MovieLibrary class

Now we can make our MovieLibrary class. As a library we want to be able to add a movie to the library, rate a movie in the library, get a single movie from the library, and get all the movies from the library.

package main.java;

public class MovieLibrary {

}

We can use a data structure to store our movies, an ArrayList would be suitable for storing each instance of Movie.

At this point it would be natural to implement an ArrayList instance field and a constructor that initialises it when an instance of MovieLibrary is created:

package main.java;

import java.util.ArrayList;

public class MovieLibrary {
    private ArrayList<Movie> movies; // Declare a variable of ArrayList type capable of holding Movie objects

    constructor
    public MovieLibrary() {
        this.movies = new ArrayList<Movie>(); Assign it as a new Arraylist object
    }

}

However, this would actually be bad practice because ArrayList is an implementation of the List interface and we would be limiting ourselves to just the ArrayList implementation of List. Therefore it would be better to do something like this:

package main.java;

import java.util.List; // import list
import java.util.ArrayList;

public class MovieLibrary {
    private List<Movie> movies; // Declare a variable of List type capable of holding Movie objects

    constructor
    public MovieLibrary() {
        this.movies = new ArrayList<Movie>(); Assign it as a new Arraylist object
    }

}

But technically we are still limiting ourselves an the ArrayList so we can impove this further by declaring what type of List to implement outsdie of the class and passing it in with dependency injection:

package main.java;

import java.util.List;

final private List<Movie> movies;

    public MovieLibrary(List<Movie> movies) { // Pass in a `List` object from outside
        this.movies = movies; // Assign it
    }

With this technique our code it much more flexible and if we wanted to change the type of List interface from ArrayList to LinkedList we can do that easily from the main class without having to refactor this class at all. This we become clear when we develop the Main class in the next section.

Next we can make our addMovie method. In this method we want to be able to add an instance of our Movie to the movies ArrayList. However, the instance of Movie is going to be created in the UI and then passed into the method:

UI class

private static MovieLibrary movieLibrary = new MovieLibrary();

...

main() {}

'''

someMethod() {
  Movie hotFuzz = new Movie("Hot Fuzz", "Edgar Wright", 9.2, 2007)

  movieLibrary.addMovie(hotFuzz) // This method
}

Therefore our addMovie() method just need to take a movie and add it to the list:

addMovie() method
public void addMovie(Movie movie) {
    this.movies.add(movie); // adds hotFuzz to the library
}

Now we want a method to set a new rating for the movie. This method will be slightly more complex because we have to find the movie in the ArrayList before we update the rating.

The rateMovie() method will take in a movie (String) and rating (Double) and loop through the movies ArrayList. If it finds an instance of movie with an equal title, then is will call the setRating() method of the movie instance.

rateMovie() method
public void rateMovie(String title, double rating) {

    // Find the movie by title and set its rating
    for (Movie movie : this.movies) {
        if (movie.getTitle().equalsIgnoreCase(title)) { // Ignoring case, are two Strings equal?
            movie.setRating(rating); // If so then we set the new rating
            break; // And break out of the loop
        }
    }

    System.out.println("Movie not found.");
}

The method above should set a new rating however it doesn't return anything so how can we check that our rating has actually been updated. We can make a getMovieByTitle(String title) method.

Once again we want to loop through the movies ArrayList, and when we get a match we return the movie:

getMovie() method
public void getMovieByTitle(String title, double rating) {

    // Find the movie by title and return it
    for (Movie movie : this.movies) {
        if (movie.getTitle().equalsIgnoreCase(title)) {
            return movie;
        }
    }

    System.out.println("Movie not found.");
}

Note return automatically breaks our the of loop so we don't need to use the break keyword.


Notice that both our rateMovie() method and our getMovieByTitle() methods use the same loop. In order to avoid repetition we can refactor our rateMovie() method to take an instance of Movie rather than a title and call the getMovieByTitle() method from our main class:

rateMovie() method refactor
public void rateMovie(Movie movie, double rating) {
    movie.setRating(rating);
}

If this doesn't make sense at the moment it should click when we get to the Main class.

Now lets create a method called removeMovie() which takes and deletes an instance of the Movie class from the ArrayList and returns nothing:

removeMovie() method
public void removeMovie(Movie movie) {
    this.movies.remove(movie);
}

And finally we can make a getMovies method to return the entire library. For this we can just return the List.

getMovies() method
public List<Movie> getMovies() {
    return this.movies;
}

Thats all we need for the MovieLibrary class, your code should look like this:

MoviesLibrary class completed
package main.java;

import java.util.List;

public class MovieLibrary {
    final private List<Movie> movies;

    public MovieLibrary(List<Movie> movies) {
        this.movies = movies;
    }

    public void addMovie(Movie movie) {
        this.movies.add(movie);
    }

    public void rateMovie(Movie movie, double rating) {
        movie.setRating(rating);
    }

    public void removeMovie(Movie movie) {
        this.movies.remove(movie);
    }

    public List<Movie> getMovies() {
        return this.movies;
    }

    public Movie getMovieByTitle(String title) {
        for (Movie movie : this.movies) {
            if (movie.getTitle().equalsIgnoreCase(title)) {
                return movie;
            }
        }
        return null;
    }
}


MovieLibraryTest class

Generate your MovieLibraryTest class and give it the boilerplate:

package main.java;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName; // import DisplayName

import static org.junit.jupiter.api.Assertions.*;

class MovieLibraryTest {

   @Test
    @DisplayName("addMovie test")
    void addMovie() {
    }

    @Test
    @DisplayName("rateMovie test")
    void rateMovie() {
    }

    @Test
    @DisplayName("removeMovie test")
    void removeMovie() {
    }

    @Test
    @DisplayName("getMovies test")
    void getMovies() {
    }

    @Test
    @DisplayName("getMovieByTitle test")
    void getMovieByTitle() {
    }
}

Import BeforeEach at the top. We want to create an instance of MovieLibrary and an empty ArrayList in our BeforeEach block. We need to pass our ArrayList into the MovieLibrary instance as an argument:

import java.util.ArrayList;

class MovieLibraryTest {

    private MovieLibrary testMovieLibrary; // MovieLibrary instance

    @BeforeEach
    void setUp() {
        ArrayList<Movie> moviesArrayList = new ArrayList<>(); // initialise ArrayList
        testMovieLibrary = new MovieLibrary(moviesArrayList); // initialise MovieLibrary instance with ArrayList
    }

   @Test
    @DisplayName("addMovie test")
    void addMovie() {
    }

    ...
}

We can start writing our addMovie Test which takes an instance of Movie. We have to use the getMovies() method to check that it has been added because the add method doesn't return anything and the movies ArrayList is private.

Call the addMovie method then check that the size of the ArrayList returned by getMovies() is 2:

addMovie test
@Test
@DisplayName("addMovie test")
void addMovie() {
    Movie testMovie = new Movie("test title", "test director", 5.0, 2021);

    testMovieLibrary.addMovie(testMovie);

    assertEquals(1, testMovieLibrary.getMovies().size());
}

The rateMovie test adds a movie with the addMovie() method, then calls the rateMovie() method, passing in the Movie instance and a new rating. We can then make our assertion using the getRating() method of the Movie instance:

rateMovie test
@Test
@DisplayName("rateMovie test")
void rateMovie() {
    Movie testMovie = new Movie("test title", "test director", 6.0, 2022);

    testMovieLibrary.addMovie(testMovie);

    testMovieLibrary.rateMovie(testMovie, 4.0);

    assertEquals(4.0, testMovie.getRating());
}

Our removeMovie test should have two assertions. We call addMovie() to add our instance, we assert that 1 getMovies() returns an ArrayList with 1 item, we call the removeMovie() method with our instance, and check that the getMovies() returns an ArrayList with 0 items:

removeMovie test
@Test
@DisplayName("removeMovie test")
void removeMovie() {
    Movie testMovie = new Movie("test title", "test director", 6.0, 2022);

    testMovieLibrary.addMovie(testMovie);

    testMovieLibrary.removeMovie(testMovie);

    assertEquals(0, testMovieLibrary.getMovies().size());
}

Our getMovies test looks pretty much the same as our addMovie test but we can add in an extra Movie instance to make sure it works for multiple values:

getMovies test
@Test
@DisplayName("getMovies test")
void getMovies() {
    Movie testMovie = new Movie("test title", "test director", 6.0, 2022);
    testMovieLibrary.addMovie(testMovie);

    Movie testMovie2 = new Movie("test title 2", "test director 2", 6.0, 2022);
    testMovieLibrary.addMovie(testMovie2);

    assertEquals(2, testMovieLibrary.getMovies().size());
}

In our getMovieByTitle test we will add a movie and then assert that it has been returned when we pass the title into the getMovieByTitle() method. We can also confirm that if we pass in an incorrect value it returns null using assertNull(). We don't need a seperate test for this because null is a value not an exception.

getMovieByTitle test
@Test
@DisplayName("getMovieByTitle test")
void getMovieByTitle() {
    Movie testMovie = new Movie("test title", "test director", 6.0, 2022);
    testMovieLibrary.addMovie(testMovie);

    assertEquals(testMovie, testMovieLibrary.getMovieByTitle("test title"));
    assertNull(testMovieLibrary.getMovieByTitle("incorrect title"));
}

We have finished our MovieLibrary and MovieLibraryTest. In the next section we are going to write our Main class.


back next