Model View Presenter relations

Model View Presenter (MVP) in Android, Part 3

Hello! In the conclusion of the Model View Presenter in Android series, we’ll talk about:

  • How to implement MVP using the library  simple-mvp to speed up your production process
  • Common hiccups when using MVP in Android

If you missed the anterior articles, or  want to understand the Model View Presenter concept in more detail, you can read it here:

Using the simple-mvp library

The simple-mvp library was implemented using Dr. Douglas C. Schmidt concept. The code follows the canonical form principles, hence it doesn’t use any library or resource from outside the Android SDK. There are some requirements that must be followed to the framework work properly.

Attention: simple-mvp is still an experimental library created for education purposes.
Some methods and operations may change until its maturity.

Quick setup

  1. add build.gradle
    compile ‘com.tinmegali.mvp:mvp:0.0.7’
  2. Create interfaces to communicate between MVP layers
    • interface RequiredViewOps extends ActivityView
      with VIEW methods to be accessed by PRESENTER
    • interface ProvidedPresenterOps extends PresenterOps
      Operations offered to VIEW to communicate with PRESENTER
    • interface RequiredPresenterOps
      with Required PRESENTER methods available to MODEL
    • interface ProvidedModelOps extends ModelOps
      Operations offered to MODEL to communicate with PRESENTER
  3. Implement MVP objects extending its generics
    • MODEL from Model View Presenter (MVP) pattern.
      class MODEL extends GenericModel
            implements MVP_MainActivity.ProvidedModelOps
      
    • VIEW layer of MVP pattern
      class VIEW_Activity extends GenericMVPActivity<MVP_MainActivity.RequiredViewOps, MVP_MainActivity.ProvidedPresenterOps, MainPresenter>
      implements MVP_MainActivity.RequiredViewOps
      

      Could also extend GenericMVPFragment

    • PRESENTER from Model View Presenter (MVP) Pattern.
      class MainPresenter extends GenericPresenter<MVP_MainActivity.RequiredPresenterOps, MVP_MainActivity.ProvidedModelOps, MVP_MainActivity.RequiredViewOps, MainModel>
      implements
      MVP_MainActivity.RequiredPresenterOps,
      MVP_MainActivity.ProvidedPresenterOps

In the git, the files inside the package /mvp/mvp are the essential to the framework. If you want to create your own version of the library, copy those files or just extend and personalize them.

Step by step guide to use simple-mvp

MVP interfaces

We’ll start implementing the interfaces responsible to create a communication between the Model View Presenter layers. You’ll need to construct four interfaces:  RequiredViewOps ,  ProvidedPresenterOps ,  RequiredPresenterOps  e  ProvidedModelOps . This sample is available on GitHub.

/**
* Required VIEW methods available to PRESENTER
* PRESENTER to VIEW
*
* Métodos obrigatórios em VIEW, disponíveis para PRESENTER
*/
interface RequiredViewOps extends ActivityView {
}


/**
* Operations offered to VIEW to communicate with PRESENTER
* VIEW to PRESENTER
*
* operações oferecidas ao layer VIEW para comunicação com PRESENTER
*/
interface ProvidedPresenterOps extends PresenterOps<RequiredViewOps> {
}

/**
* Required PRESENTER methods available to MODEL
* MODEL to PRESENTER
*
* operações oferecidas pelo layer PRESENTER para comunicações com MODEL
*/
interface RequiredPresenterOps {
}

/**
* Operations offered to MODEL to communicate with PRESENTER
* PRESENTER to MODEL
*
* operações oferecidos pelo layer MODEL para comunicações com PRESENTER
*/
interface ProvidedModelOps extends ModelOps<RequiredPresenterOps> {
}

Model, View and Presenter Objects

All layers have its generic objects to facilitate the implementation. This classes execute most of the heavy lifting to configure the MVP. You’ll only need to initialize it object and the job is done.

MODEL

public class MainModel extends GenericModel<MVP_MainActivity.RequiredPresenterOps>
        implements MVP_MainActivity.ProvidedModelOps {

    /**
     * Method that recovers a reference to the PRESENTER
     * - You must ALWAYS call {@link super#onCreate(Object)} here
     * @param presenterOps Presenter interface
     */
    @Override
    public void onCreate(MVP_MainActivity.RequiredPresenterOps presenterOps) {
        super.onCreate(presenterOps);
        // initialize objects
    }

    /**
     * Called by layer PRESENTER when VIEW pass for a reconstruction/destruction.
     * Usefull for kill/stop activities that could be running on the background
     * Threads
     * @param isChangingConfiguration Informs that a change is occurring on configuration
     */
    @Override
    public void onDestroy(boolean isChangingConfiguration) {
       // kill or stop actions
    }
}

PRESENTER

public class MainPresenter
        extends GenericPresenter<MVP_MainActivity.RequiredPresenterOps,
                                MVP_MainActivity.ProvidedModelOps, 
                                MVP_MainActivity.RequiredViewOps,
                                MainModel>
        implements
            MVP_MainActivity.RequiredPresenterOps,
            MVP_MainActivity.ProvidedPresenterOps
{

    /**
     * Operation called during VIEW creation in
     * {@link com.tinmegali.mvp.mvp.GenericMVPActivity#onCreate(Class, Object)} </br>
     * Responsible to initialize MODEL.
     * @param view  The current VIEW instance
     */
    @Override
    public void onCreate(MVP_MainActivity.RequiredViewOps view) {
        super.onCreate(MainModel.class, this);
        // super.onCreate(<Model.class>, <RequiredPresenterOps>);
        setView( view );
    }

    /**
     * Operation called by VIEW after its reconstruction.
     * @param view  The current VIEW instance
     */
    @Override
    public void onConfigurationChange(MVP_MainActivity.RequiredViewOps view) {
        setView(view);
    }
}

VIEW

/*
 * VIEW layer of MVP pattern *
 * Layer VIEW no padrão Model View Presenter (MVP)
 */
public class MainActivity
        extends
        GenericMVPActivity<MVP_MainActivity.RequiredViewOps,
                MVP_MainActivity.ProvidedPresenterOps,
                                            MainPresenter>
        implements
            MVP_MainActivity.RequiredViewOps{

    /**
     * Method that initialized MVP objects
     * {@link super#onCreate(Class, Object)} should always be called
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // super método obrigatório
        super.onCreate(MainPresenter.class,this);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

    // View operations
}

Avoiding problems

  • When accessing getView()  always check if it is available, since VIEW depends is directly affected by lifeCycle, it could be momentarily destroyed.
  • Use multiple PRESENTER when working with complex views
  • If you’ll use multiple PRESENTER the easiest way to pass data between them, is using some kind of EventBus
  • Always call  super.onCreate(<Presenter.class>,<RequiredViewOps>);  when overriding onCreate(Bundle savedInstanceState)  in VIEW layer.
  • Always call super.onCreate(<Model.class>, <RequiredPresenterOps>);  and setView( view );  when overriding onCreate(MVP_MainActivity.RequiredViewOps view)  in PRESENTER layer
  • Always redefine view  in PRESENTER layer during configuration changes
    @Override
    public void onConfigurationChange(MVP_MainActivity.RequiredViewOps view) {
       setView(view);
    }

Unit Testing

I heard a lot of good feedback about this article, but some people were having trouble testing the layers, specially the Presenter. This should be expected, since I talked so much about testing and hasn’t done none over here. I’m sorry guys! But let’s try to address this.

Testing the Presenter

You can test the Presenter without any problems. Try to avoid calling the onCreate methods.  Those methods are responsible to instantiate Model and get a View reference, so if you want to make test as independent as can be, you should skip that. Just create the Presenter using an empty constructor, and you’re done.

mPresenter = new MainPresenter();

Since Presenter’s role is to work as middle man, your test could need to mock Model or View. If this is the case, you could use .setView and .testWithModel (a helper method exclusive to mock Model in Presenter).

  • Using a mock of Model in Presenter
mModel = mock(MainModel.class);
mPresenter.testWithModel(mModel);
  • Using a mock of View in Presenter
mView = mock(MainActivity.class);
mPresenter.setView(mView);

Testing the Model

The layer Model test setup is more straightforward. If you need to pass a Presenter mock, you can call onCreate passing the mock reference.

mModel = new MainModel();
mPresenter = mock(MainPresenter.class);
mModel.onCreate(mPresenter);

Once you’ve done that, you are good to go. You can check this two small tests as a reference.

Feel free to ask question and suggest other themes for the following articles.

See you soon!


Also published on Medium.

5 thoughts on “Model View Presenter (MVP) in Android, Part 3”

  1. Hi,
    In this case the presenter is responsible to get instances of all his model dependencies which makes it harder to write tests, especially unit tests.
    There are many examples which state the the View should pass all dependencies to the Presenter.
    On one hand it defies the MVP rule and makes the view aware of the models but on the other it helps you to pass mocked models to the Presenter and thus makes easier to test.
    Since i didn’t see any tests in your Git project i would love to hear your opinion on this issue.

    1. Hello DanV,
      First of all, thanks for the feedback!

      My bad, I should really add some test in the git. I’ll update the git with a test for reference.
      Although, I can’t see the difficulty that you mentioned.
      Yes, the Model instance is really created by the Presenter, but by my understanding this doesn’t mess up the tests.
      I mean, if you use Mockito, you can test it just fine.
      Take a look at that snippet.

      @RunWith(RobolectricGradleTestRunner.class)
      @Config(constants = BuildConfig.class, sdk = 21, manifest = "/src/main/AndroidManifest.xml")
      public class ModelTest {
          private MainPresenter mPresenter;
          private MainModel mModel;
      
          @Before
          public void setup(){
              mPresenter = mock(MainPresenter.class, CALLS_REAL_METHODS);
              mModel = mock(MainModel.class, CALLS_REAL_METHODS);
      
              when(mModel.getPresenter()).thenReturn(mPresenter);
              when(mPresenter.getModel()).thenReturn(mModel);
          }
      
          @Test
          public void simpleTest(){
              String name = "simple_mvp";
              // Add name
              MVP_MainActivity.ProvidedPresenterOps presenterOps = mPresenter;
              presenterOps.clickSaveName(name);
              verify(mPresenter.getModel(), atLeastOnce()).saveName(anyString());
              verify(mPresenter).onNameSaved(name);
      
              // Clear name
              presenterOps.clickClearName();
              verify(mPresenter.getModel(), atLeastOnce()).clearName();
              verify(mPresenter).onNameCleared();
          }
      }
      

      You could create the Model and the Presenter in the View and then pass the Model reference to the Presenter, but I don’t see why this would be necessary.
      If you see a specific situation where my implementation could be a problem, please share with me and we can discuss more about.
      Thanks for the input.

      1. Hi,
        Thank you for the quick answer.

        Please take a look at Mockito.CALLS_REAL_METHODS JavaDoc.
        It states that it is not a good practice to use this method “* However, there are rare cases when partial mocks come handy:
        * dealing with code you cannot change easily (3rd party interfaces, interim refactoring of legacy code etc.)
        * However, I wouldn’t use partial mocks for new, test-driven & well-designed code.”

        However, it is not my problem.
        i would like to test the presenter as a standalone unit using unit tests (not instrumentation)
        my specific problem is that i have a static class DependencyInjector that provides the models to the entire app.
        DependencyInjector.getsCrashReporter(), DependencyInjector.getDirectionsManager() and so on..
        Is it a good practice to set mocked models in the DependencyInjector and how do i ensure that the DependencyInjector restarted properly after each test?

        Thank you!

        1. Hello again DanV,
          I’m aware about this alert in Mockito docs, I chose to use it for simplicity. But it is completely possible to test Presenter and Model with JUnit without a problem, unless the method tested needs to access Model, in that case you’ll need to provide some kind of mock.

          I don’t know exactly what DependencyInjector you’re using, but as far as Dagger goes, static injection isn’t a good practice, especially when it comes to testing.

          Warning: This feature should be used sparingly because static dependencies are difficult to test and reuse.

          Taken from Dagger docs

          That said, I don’t know if the practice that you mentioned is a good one, but who am I to tell? lol
          Take a look at this article, it a good guide to test depedency injection using Roboeletric. I tend to use RoboEletric in my tests, because it provides a framework with a lot of Android classes mocked.

          I didn’t use Dependency Injection on simple-mvp to maintain the library without any external influences other than Android SDK and Java. Some could argue that adding DI would make it more productive and maybe they’re right. But my main goal in this series and on the library was to illustrate MVP concept, hence the code doesn’t use any external libs.

          Maybe this discussion concerning DI is a little bit offtopic. I fear that it could confuse somebody out there. I’m available if you need to discuss more, but can you be so kind to send an email to tinmegali@gmail.com with further questions? I’ll answer as soon as I can.
          Tks again for the input!

Leave a Reply

Your email address will not be published. Required fields are marked *