TheMovieDb API – Part 4 – The Movie Presenter

TheMovieDb API android app screenshots

Welcome back to this series on implementing TheMovieDb API. As always remember all the code for this series will be in this GitHub repo today we are going to setup and test our Movie Presenter, remember the MVP pattern? Awesome so let’s get right to it!

First let’s define what we want to do with a simple interface. We need to be able from the View to: show the progress bar, show details of the movie later on, set the movie lists to our adapter, clear movies out and show messages. So it should look something like:

public interface MoviesContract {
    /**
     * Represents the methods that our view (Activity or Fragment)
     * will implement.
     */
    interface View {

        void showProgressBar(boolean active);

        void showMovieDetailUi(Bundle movieArgs);

        void setMovies(List<Movie> movies);

        void clearMovies();

        void showMessage(String msg, int duration);
    }
...

This View interface will be implemented either at our Activity/Fragment of our choose, but who calls them? Well it works like this, the Activity/Fragment has an instance of the MoviePresenter in which implements another interface called UserActionsListener:

/**
 * Represents the method calls that our presenter will implement,
 * as the interface name implies, this are user based actions or similar that trigger
 * actions on the presenter, the presenter then will callback using it's
 * reference on a MoviesContract.View member.
 */
interface UserActionsListener {

    int getResultPageNumber();

    void setProgressBar(boolean active);

    void loadMovies();

    void openMovieDetails(@NonNull Movie requestedMovie);
}

You may say it’s like going back and forward, indeed it is, this is one of the things of the MVP pattern but as a good thing we get to test it and even mock it with a library called mockitoLet’s now see how our MoviePresenter looks like;

First we make it extend our BasePresenter and implement our MoviesContrat.UserActionsListener. We also place initial member variables to be used within it and a constructor which will take our MovieService and a movies view contract (the View interface).

public class MoviesPresenter extends BasePresenter implements MoviesContract.UserActionsListener {

    public final static String TAG = "MoviesPresenter";

    private int resultsPage = 1;
    private boolean fetchingMovies = false;
    private MovieServiceInterface movieService;
    private MoviesContract.View moviesViewContract;


    public MoviesPresenter(@NonNull MovieServiceInterface movieService, @NonNull MoviesContract.View movieView) {
        this.movieService = movieService;
        this.moviesViewContract = movieView;
    }

...

Good as soon as we tell it to implement the UserActionsListener interface Android Studio powerful auto complete kicks in and do most of the heavy work for us, here is a finished implementation of each method:

@Override
public void loadMovies() {
    if (fetchingMovies) {
        return;
    }

    //not fetching? Proceed.
    fetchingMovies = true;
    movieService.nowPlaying(resultsPage).enqueue(new Callback<MovieResultsPage>() {
        @Override
        public void onResponse(Call<MovieResultsPage> call, Response<MovieResultsPage> response) {
            if (response.isSuccessful()) {
                moviesViewContract.setMovies(response.body().results);
                moviesViewContract.showProgressBar(false);
                resultsPage++;
                fetchingMovies = false;
            }
        }

        @Override
        public void onFailure(Call<MovieResultsPage> call, Throwable t) {
            fetchingMovies = false;
            t.printStackTrace();
            moviesViewContract.showMessage("An error occured while fetching movies", Snackbar.LENGTH_SHORT);
        }
    });
}

loadMovies() is the most important methhod implemented here so let’s take it first. We first are checking if we are fetchingMovies if not we move ahead and use the movieService reference to call the nowPlaying endpoint within it, all of this off the main thread of course to prevent the infamous “Network call on MainThread ex” or so it goes. If all is good we just pass “the view” (represented by the movieViewContract) the movies. Easy right?

@Override
public void openMovieDetails(@NonNull Movie requestedMovie) {
    Bundle movieArgs = new Bundle();
    movieArgs.putParcelable(Movie.PARzCELABLE_KEY, requestedMovie);
    moviesViewContract.showMovieDetailUi(movieArgs);
}

openMovieDetails() takes in a movie and returns a bundled version of it, we use the parcelable serialization to make things even easier since its optimized for Android. *Make sure to have your Movie.class updated to implement the Parcelable class and the IDE should help you out with the rest, trust me it is pretty straight forward.

@Override
public int getResultPageNumber() {
    return resultsPage;
}

@Override
public void setProgressBar(boolean active) {
    moviesViewContract.showProgressBar(active);
}

These last two are self explanatory.

Now unit testing time, remember with test driven development we can see how things should work and behave without tightly coupling things altogether. We are now going to make some assertions and mocks of our presenter. Under app/src/test/java/your.package create a class called MoviePresenterTests:

public class MoviePresenterTests {
    private static List<com.jrosa.moviezone.domain.Movie> movieMocks = new ArrayList();
    private MoviesPresenter moviesPresenter;

    @Mock
    MoviesContract.View moviesView;

Awesome, we are going to mock several methods of the MoviesContract.View interface and assert methods from the MoviesPresenter. First let’s setup the movie presenter and initialize mocks:

@Before
public void setupNotesPresenter() {
    // Mockito has a very convenient way to inject mocks by using the @Mock annotation. To
    // inject the mocks in the test the initMocks method needs to be called.
    MockitoAnnotations.initMocks(this);

    MovieServiceInterface movieService = ApiClient.createService(MovieServiceInterface.class);
    // Get a reference to the class under test
    moviesPresenter = new MoviesPresenter(movieService, moviesView);
}

@Before
public void setUp() {
    //Add a few movies to our {@movieMocks} collection.
    com.jrosa.moviezone.domain.Movie movie = new com.jrosa.moviezone.domain.Movie();
    movie.id = 12345;
    movie.title = "foo";

    com.jrosa.moviezone.domain.Movie movie2 = new com.jrosa.moviezone.domain.Movie();
    movie2.id = 6789;
    movie2.title = "bar";

    movieMocks.add(movie);
    movieMocks.add(movie2);
}

Simple right?

Now let’s mock the showMessage method:

@Test
public void movieView_showMessage() {
    //Given the following message
    moviesView.showMessage("foo", 1);

    //Message should has been showed.
    verify(moviesView).showMessage("foo", 1);
}

Cool, what about setting movies, can we mock it too?

@Test
public void movieView_setMovies() {
    //set a predefined mocked movie list.
    moviesView.setMovies(movieMocks);

    //Check that the movie list set is the mocked list.
    verify(moviesView).setMovies(movieMocks);
}

Awesome! What about the progress indicator, clearing the movies and asserting the movies getResultPageNumber method?

@Test
public void movieView_setProgressIndicator() {
    //Set progress indicator visibility
    moviesView.showProgressBar(true);

    //Check that it was successfully set.
    verify(moviesView).showProgressBar(true);
}

@Test
public void movieView_clear() {
    //clear movie list.
    moviesView.clearMovies();

    //Check that it was successfully clear.
    verify(moviesView).clearMovies();
}

@Test
public void setMoviesPresenter_getResultPageTest() {
    Assert.assertTrue(moviesPresenter.getResultPageNumber() >= 1);
}

You are becoming good at this! Run them!

Mockito tests
Mockito tests

See? That was not that hard, until next time!

Joel

Please follow and like us:
Facebook
Twitter
Joel Sosa

Author: Joel Sosa

Android nanodegree holder | Graduate Student @GeorgiaTech CS Interactive Intelligence | @gdgpuertorico Organizer

1 thought on “TheMovieDb API – Part 4 – The Movie Presenter”

Comments are closed.