Testing Android MVP- 8 mins
There are thousands of articles explaining the good, bad and ugly of different programming patterns for Android, sadly most of the articles found online forget to explain one last important step: Testing.
Choosing between one pattern or another is both a matter of personal preference and project requirements. I don’t believe MVP is superior to MVVM or a full custom solution, however its simplicity is the reason it is my pattern to-go when creating a new screen.
MVP in four bullet points
- MVP stands for Model-View-Presenter.
- The “Model” is your data source, doesn’t matter if a database, network calls or a hard-coded list of objects.
- The “View” is your Activity, Fragment or custom Android View. Which should just take care of displaying things and handling user interaction.
- The “Presenter” is a plain old java object (POJO) that handles the communication between the View and the Model, makes any required data transformations, handles errors from the Model and tries to remove as much UX logic from the View as possible.
Testing principles for MVP
- Our main principle is that we will be using JUnit and not Espresso or any other automated testing framework.
- The second principle is that we will test each part separately, as opposed to an integration test. So knowledge on dependency injection frameworks will be required.
- Because we will be using JUnit, we will need to mock all UI components. For that we will use Robolectric.
- Being familiar with Mockito will be also necessary in order to test interactions and mock classes.
Testing the Model
Testing the Model is something we should be doing independently if we are doing MVP or no. In some cases, our Model will be a 3rd party library we can’t test, so we should be able to provide an easy to mock interface.
- The Model must not hold any reference to the Presenter or the View.
- The Model should be provided to the Presenter as an easy mockable Interface, specially when the Model is part of a 3rd party library.
- The Model should be tested independently of the design pattern used.
Let’s follow an example for the full article. The Model is class that provides the user profile as an Observable (we don’t know if it comes from a local database or from the Internet), and it is defined with an Interface.
I would test this by using the TestSubscriber from RxJava and making sure that I get the expected value with no errors.
Testing the View
Testing the View is easier than we think, the most complex part is the Robolectric setup, but after doing it once, doing the same for all views will be easy.
Like any other MVP implementation, here we have the View Interface:
Our example View will be a custom Android View that extends FrameLayout
What should we test here? EVERYTHING!
- Let’s test that the View gets correctly created.
- Let’s test that the default values are correct.
- Let’s text that the user interactions get to the Presenter.
- Let’s test that the View is doing its main task (display the UserProfile)
And here is the test class:
Step by Step:
- The top annotations is part of the Robolectric configuration. What we are telling the JUnit is to use Robolectric test runner with the standard BuildConfig (which we are not changing).
- We hold an instance of our custom view, which will be testing.
- We create a mocked Presenter using Mockito. We only want it to verify that the View is calling the Presenter when it should.
- We create a new instance of the ProfileFrameLayout using the RuntimeEnviroment from Robolectric to get the context from the application.
- We set our mocked Presenter, as we want to make sure it is called back.
- TestEmpty checks two things: That the attachView method was called and that the textUsername is still empty.
- TestLeaveView checks that the detachView method is called when the View obtains that Android event. In case of an Activity, you would have here onDestroy or onPause, depending on your case.
- TestReturnToView does the same for the other event. Note that I reset the Presenter mock counters because the attachView method was called at the beginning.
- Finally TestDisplay tests that the values we pass through the display() methods are set correctly on the TextView.
We have covered all lines of code from our View, and it really doesn’t get more complicated than this once you start adding more and more logic and UI elements.
- Provide a mocked Presenter so you can verify events and interaction thanks to Mockito.
- Provide access to the internal views so you can verify their values.
- Create an instance of your View without dependencies to the Android framework thanks to Robolectric.
Testing the Presenter
We will test the Presenter the same way we tested the Model, with a simple JUnit test. However this time we will provide a mocked View to verify that the Presenter is communicating correctly with the View.
Our Presenter will receive a View and obtain the data from the Model to display it on the View.
The example ProfilePresenter provided is not handling a lot of the RxJava Gotchas you should take care of, because for the brevity of this article they have been removed.
What we want to test here is that the Model (ProfileInteractor) and the View receive the correct calls from the Presenter. We are actually not interested in verifying the data, but if your Presenter was doing some data transformation you should test that as well.
You should test also how the Presenter will handle errors from the Model. With RxJava you can do that by returning an Observable.error(new Throwable()) from the getUserProfile().
Step by step:
- Create mocked instances of the Model and the View.
- Mock the getUserProfile method output. I use when() to tell the mocked interactor to return an Observable from an empty instance of the expected result, using just() method to create it. (you could also return Observable.error() to test error handling)
- Pass those to your instance of the Presenter.
- The method getUserProfile gets called when the View is attached, so on our test we only need to verify that this method was called.
- Same for the View, we need to verify that the method was called. And we actually don’t care about the content of what we got, that’s why I use any() as matcher.
- Mock your Model and your View so you can verify they get the calls from your Presenter.
- Fake responses from the Model to test the different scenarios. You don’t even need to use RxJava.
- Have a mockable Model, if not, wrap it.
- Use dependency injection to provide a mocked Presenter to your View. Do NOT create the Presenter inside the View.
- Test not only the outputs, but the interactions too.
- Test the View life-cycle if your Presenter depend on it.
- Test visual changes from your View, not only the text, but also visibility or background color if you change it.
- Test how your Presenter will handle the different responses from the Model.
I hope that next time you are writing an App and using the MVP programming pattern, you will do it with these concepts in mind so you will create fast unit tests.