Chapters

Hide chapters

Advanced Android App Architecture

First Edition · Android 9 · Kotlin 1.3 · Android Studio 3.2

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

16. Testing VIPER
Written by Aldo Olivares

Heads up... You’re accessing parts of this content for free, with some sections shown as xxpaqhpag text.

Heads up... You’re accessing parts of this content for free, with some sections shown as sqxiwwwuq text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In the last chapter, you refactored WeWatch to use the View-Interactor-Presenter-Entity-Router (better know as VIPER) architecture pattern. In this chapter, you’ll learn how to test your VIPER architecture by creating unit tests and mocking classes using Mockito.

Getting started

Start by opening the starter project and selecting the app build.gradle from the project.

Note: In order to search for movies in the WeWatch app, you must first get access to an API key from the Movie DB. To get your API own key, sign up for an account at www.themoviedb.org. Then, navigate to your account settings on the website, view your settings for the API, and register for a developer API key. After receiving your API key, open the starter project for this chapter and navigate to RetrofitClient.kt. There, you can replace the existing value for API_KEY with your own.

After the starter project finishes loading and building, run the app on a device or emulator and try adding a new movie by tapping the + float action button.

Type in a title and press the icon to the right to search for a movie.

Nothing shows up, indicating there’s some kind of problem.

At this point, it’s unclear whether there’s an issue with the search function or the TMDB API. Try adding a movie manually using Add Movie. Notice the main screen is still empty.

Something is clearly wrong — and thanks to unit testing, you’ll be able to find out what it is by creating unit tests for each of your Presenters.

Testing your Main presenter

For this project, you’ll use Mockito to mock the necessary dependencies for your app, and you’ll use JUnit to perform the tests.

Heads up... You’re accessing parts of this content for free, with some sections shown as wzqulqtom text.

Heads up... You’re accessing parts of this content for free, with some sections shown as rbdozcfyj text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
testImplementation 'junit:junit:4.12'
testImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.5.0'

Heads up... You’re accessing parts of this content for free, with some sections shown as gszyvcxyx text.

Heads up... You’re accessing parts of this content for free, with some sections shown as fdzuqlhyv text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
@RunWith(MockitoJUnitRunner::class)
class MainPresenterTest {
    private lateinit var presenter: MainPresenter
}
@Mock
private lateinit var view: MainContract.View
@Mock
private lateinit var interactor: MainContract.Interactor
//1
@Before
fun setup() {
  //2
  val cicerone = Cicerone.create()
  //4
  presenter = MainPresenter(view, interactor, cicerone.router)
}
@Test
fun displayMoviesOnQuerySuccess() {
  //1
  val movieList = listOf(Movie())
  //2
  presenter.onQuerySuccess(movieList)
  //3
  verify(view).displayMovieList(movieList)
}

Heads up... You’re accessing parts of this content for free, with some sections shown as jlnewdpid text.

Heads up... You’re accessing parts of this content for free, with some sections shown as flgahmdyc text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

@Test
fun loadMovies() {
  presenter.onViewCreated()
  verify(interactor).loadMovieList()
}

Heads up... You’re accessing parts of this content for free, with some sections shown as flvexrgip text.

Heads up... You’re accessing parts of this content for free, with some sections shown as tsbafcnyh text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Testing the AddMovie presenter

In this section, you’ll create a test class for AddMoviePresenter.

private lateinit var presenter: AddPresenter
@Mock
private lateinit var view: AddContract.View
@Mock
private lateinit var interactor: AddContract.Interactor

@Before
fun setup() {

  val cicerone = Cicerone.create()
  val router = cicerone.router

  presenter = AddPresenter(view, interactor, router)
}
@Test
fun addMovieWithTitle() {
  val movie = Movie(title = "Test Movie", releaseDate = "1991")
  presenter.addMovies(movie.title!!, movie.releaseDate!!)
  verify(interactor).addMovie(movie)
}

Wanted but not invoked:
interactor.addMovie

There were zero interactions with this mock.

Heads up... You’re accessing parts of this content for free, with some sections shown as qbnyfbjih text.

Heads up... You’re accessing parts of this content for free, with some sections shown as hcdewnpyd text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
val movie = Movie(title = title, releaseDate = year)
interactor?.addMovie(movie)

Heads up... You’re accessing parts of this content for free, with some sections shown as swnywtxaw text.

Heads up... You’re accessing parts of this content for free, with some sections shown as schiqqmam text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Testing SearchMovie Presenter

It’s time to create a test class for SearchPresenter following the usual steps.

@Test
fun displayMovieListAndHideLoadingOnQuerySuccess() {
  val movieList = listOf(Movie(title = "Best Movie"))
  presenter.onQuerySuccess(movieList)
  verify(view).hideLoading()
  verify(view).displayMovieList(movieList)
}

Wanted but not invoked:
view.displayMovieList

However, there was exactly 1 interaction with this mock:
view.hideLoading();
override fun onQuerySuccess(data: List<Movie>) {
  view?.hideLoading()
}

Heads up... You’re accessing parts of this content for free, with some sections shown as zspicctez text.

Heads up... You’re accessing parts of this content for free, with some sections shown as kdwutqmor text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
view?.displayMovieList(data)

Heads up... You’re accessing parts of this content for free, with some sections shown as rmfivnrad text.

Heads up... You’re accessing parts of this content for free, with some sections shown as wtrolpbab text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2025 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as rscalzjif text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now