Today (well, for a year or so), MVP seems to be the bees knees of Android development - or at least everyone talks about it quite a bit. However, quite often people are just as intrigued as they are confused. How do you actually do MVP in Android? What about testing it? In this post, I’ll show you how to whip out an MVP architecture for your app. I’ll even combine it with a touch of TDD - because I just love unit testing!

MVP Primer

MVP is an abbreviation of Model-View-Presenter. This is a rather simple architecture: these all three are layers in your application, with well-structured responsibilities.

Model equals data. This layer contains your Java classes for data, as well as the logic for acquiring it (e.g. fetching from a server or reading from a file). This layer can also contain some helper methods and utilities, but that’s that - no other logic here whatsoever.

View is everything you show to your user. This layer should be as thin as possible: It should be about buttons and labels. When the user interacts with something on the view, the view shouldn’t “do” anything - it should just pass tell the Presenter that this thing happened.

Presenter is the core element. When the View tells the presenter something happened, the presenter reacts accordingly. For example, View could tell presenter that all of UI is now up and running, and Presenter chooses to react by downloading a list of blog posts from a server. Meanwhile, Presenter also tells View to show a loading indicator. When the Model layer is done fetching the data, Presenter may manipulate it further to make it more presentable to the user, and finally invokes View with the data. The very last thing in the whole shebang is the View showing the data to the user.

In “pure” MVP, the View should not know anything about the Model, but this is something that’s a tad debatable. The main idea is to separate the data and the UI, all the while making things testable.

MVP In Android

The most common way of implementing MVP in Android is to make either activities or fragments to implement the View layer. This makes our View less view-y than on other platforms since activities and fragments are also responsible for handling lifecycle events, navigation and so on. However, this is not a big issue if you are careful.

TDD Primer

TDD stands for Test-Driven Development. Basically, it means that you first create a test, make sure it fails, and then write the minimum amount of code that makes the test pass. Then you re-work your test, adding more stuff to it, making it fail again, and then you re-work your code. Rinse and repeat until your feature is done!

Why TDD though? Well, first off, it ensures you write your tests. People always come up with excuses for why they didn’t write tests at all. Second, writing tests beforehand makes you think of the code in such way that makes it testable. Often times when you blindly write your app and try to write the tests afterwards, you face the fact that you simply can’t test the code! It’s too tightly coupled, it’s dependent of a thing that’s not available during testing…

Third and most important thing is that writing tests beforehand ensures your code does what it’s supposed to. Your tests should represent the use case or the business value of what you’re developing, and your code should do just that. You should be coding against the tests, not testing against the code, because in that case you are just validating that your code works, but you don’t verify that it does the right thing. It might even be impossible to verify the code anymore.

TDD + MVP In Android

Combining TDD and MVP in Android means that we’re writing tests first, but unfortunately we are not going to test everything. We’ll leave the View layer untested for now (since it’s mainly UI testing anyway), and concentrate our efforts on the presenter instead. The model layer can be tested as needed, depending of your application. In this example, I’ll use simple POJO (Plain Old Java Object) models that don’t really need any testing (unless you want to bump the coverage up).

So, let’s start!

First Iteration - Presenter

Let’s begin by creating a new Android project with an empty activity. After you’re done with the wizard, you should be represented by your main activity like this:

package me.manabreak.mvptest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Next, we’re going to write the presenter. However, let’s not start by coding - instead, let’s create the test first! So, create a new class in under the test package named MyPresenterTest.

Now, before writing any tests, you should have a good hunch of what the presenter should be doing. In our example, the view will show a list of items to the user, so the presenter should probably be loading the items and offering them to the view. Great! Let’s write a test for that:

public class MyPresenterTest {

    @Test
    public void testLoadItems() {
        MyPresenter p = new MyPresenter();
        p.load();
    }
}

But… We don’t have a MyPresenter class, not to mention a method in that class. Which is good! This is our first step in TDD - we have a test that fails. In TDD, you should be writing tests only as long as you get your first failure. Missing classes, missing methods, or just a failing unit test are all failures in this sense.

Let’s move on and create a new class called MyPresenter, and create a method called load() there. Now, let’s run our test and ensure that it passes:

First Test - Success

Whoo! We just completed our very first cycle of TDD. Now, it’s just iterating until our feature is complete. From our test’s point-of-view, we don’t care what happens inside load() - it could stay empty for all it cares, as long as it passes. So, let’s continue on our MVP implementation and think of the presenter as “done” for now - you’ll soon see why it’s not quite done yet. :)

Second Iteration - View

Okay, let’s start the next iteration. Now we want to test that the view is presented with the data, so let’s pass the view to the presenter as a constructor parameter:

@Test
public void testLoadItems() {
    MyPresenter p = new MyPresenter(view);
    p.load();
}

Aaaaand we’re back with the missing class again. We don’t have a view object, and we don’t even know what type it is! Let’s solve the problem by creating one. We don’t want to be dependent of the Android platform to run our tests, so let’s abstract the activity away by using an interface for the view:

public interface MyView {
}

Now, let’s create a view for our testing purposes and pass that to the presenter:

@Test
public void testLoadItems() {
    // Note that the view has an empty body for now
    MyView view = new MyView() {};
    MyPresenter p = new MyPresenter(view);
    p.load();
}

We are still getting an error because the presenter doesn’t have a proper constructor yet. Let’s create a constructor that takes the view as a parameter:

public class MyPresenter {

    // Our precious view
    private MyView view;

    // Our constructor that binds the view
    public MyPresenter(MyView view) {
        this.view = view;
    }

    // Our smart-ass loading method
    public void load() {

    }
}

We can now run the test and notice that it works, but… How do we ensure that the presenter called the view? We know it didn’t, though, but how can we be sure from the test’s point-of-view?

Third Iteration - Actually Coding Something

We can use the view in our test to ensure that the view was actually called with actual data. For this, we’ll create a new method in our view interface called showItems(), which is called by the presenter to present the loaded items on the UI:

public interface MyView {
    void showItems(List<String> items);
}

Let’s implement this in our test. To ensure that something happens, the view passes the received data back to the test so we can verify it.

@Test
public void testLoadItems() {
    // We'll be verifying the contents of this list!
    final List<String> itemsToVerify = new ArrayList<>();

    MyView view = new MyView() {
        // This should be called by the presenter when the
        // items are loaded and ready to be shown on the UI
        @Override
        public void showItems(List<String> items) {
            // Copy all the items to our other list
            itemsToVerify.addAll(items);
        }
    };

    MyPresenter p = new MyPresenter(view);
    p.load();

    // Let's verify that there's exactly three items here!
    assertEquals(3, itemsToVerify.size());
}

Phew, our test just grew quite a bit! Here, our view now implements the one and only method we have so far. When the presenter calls it, the list of items is copied to the itemsToVerify list. After we have called p.load(), we anticipate that the view is invoked and the list is copied, so we want to verify the results. The very last line does exactly just that - it ensures that there are now three items in the list. Is there? Let’s find out! Run the test again and see for yourself:

java.lang.AssertionError:
Expected :3
Actual   :0

Surprise! Of course there’s no items, because the view is never invoked! Not to mention the presenter never does anything! Now we finally get to write the presenter!

So, let’s go and write the code that satisfies this test - but remember, don’t write more code than absolutely necessary!

public class MyPresenter {

    private MyView view;

    public MyPresenter(MyView view) {
        this.view = view;
    }

    // Invokes the view with a list of three strings
    public void load() {
        List<String> items = new ArrayList<>();
        items.add("First");
        items.add("Second");
        items.add("Third");
        view.showItems(items);
    }
}

There we go! A simple list of three strings. Re-run the test and you should see the test passed. Nice job!

You might be wondering now: “What about the model? Where’s that?” In our example here, the whole model layer consists of the String class. Basically, the layer doesn’t even exist for now. Hmm… Hold on. How do we ensure the items we got passed to the view are the correct ones when the presenter is the only one who knows about them?

Fourth Iteration - Models

The answer is, we’ll implement our model layer. The model layer offers the items to the presenter layer, so quite obviously we start by passing another constructor parameter, just like in the case of the view. Let’s call our model layer service:

// This is the only line that changed - we added the 'service' argument
MyPresenter p = new MyPresenter(view, service);
p.load();

Just like with the view, we’ll also create an interface for this. To save some time, let’s also add the method to fetch the items here already:

public interface MyService {
    List<String> getItems();
}

Back to the test: Let’s create an object to represent our service and implement the interface. We will feed the service with a test list of items.

final List<String> serviceItems = new ArrayList<>();
serviceItems.add("A");
serviceItems.add("B");
serviceItems.add("C");
MyService service = new MyService() {
    @Override
    public List<String> getItems() {
        return serviceItems;
    }
};
MyPresenter p = new MyPresenter(view, service);
p.load();

One more thing to fix: Change the constructor of the presenter to accept two arguments:

private MyView view;
private MyService service;

public MyPresenter(MyView view, MyService service) {
    this.view = view;
    this.service = service;
}

To re-cap, the whole test now looks like this:

@Test
public void testLoadItems() {
    // We'll be verifying the contents of this list!
    final List<String> itemsToVerify = new ArrayList<>();

    MyView view = new MyView() {
        // This should be called by the presenter when the
        // items are loaded and ready to be shown on the UI
        @Override
        public void showItems(List<String> items) {
            // Copy all the items to our other list
            itemsToVerify.addAll(items);
        }
    };

    final List<String> serviceItems = new ArrayList<>();
    serviceItems.add("A");
    serviceItems.add("B");
    serviceItems.add("C");
    MyService service = new MyService() {
        @Override
        public List<String> getItems() {
            return serviceItems;
        }
    };
    MyPresenter p = new MyPresenter(view, service);
    p.load();

    // Let's verify that there's exactly three items here!
    assertEquals(3, itemsToVerify.size());
}

You can run this test and you’ll see that it works just fine! Now, let’s add three more assertions to the end of it to see that we really get the three strings A, B and C our service returns.

assertEquals("A", itemsToVerify.get(0));
assertEquals("B", itemsToVerify.get(1));
assertEquals("C", itemsToVerify.get(2));

Now we run the test, and… Uh oh:

org.junit.ComparisonFailure:
Expected :A
Actual   :First

Hold on, our presenter doesn’t yet ask the service for the items! Let’s change that:

// Invokes the view with a list
public void load() {
    List<String> items = service.getItems();
    view.showItems(items);
}

Ta-dah! Your test should be passing now. Now we can do end-to-end verification through our MVP: The view invokes presenter, presenter fetches data from the model layer, and passes it to the view. And we haven’t even written any Android stuff yet!

Bring in the Android!

Now we have some fully tested code powering up our MVP, so we can go and wire it up in our activity. First off, let’s make a crude UI that hold a button and three text views. The idea is that when the user taps the button, the presenter will fetch the three strings and pass them back to the view, which will then populate the text views using them.

So, open the activity_main.xml layout and make it look like below. Note that the root element is a LinearLayout.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <!-- The button to load the items -->
    <Button
        android:id="@+id/button_load_items"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Load Items" />

    <!-- The first item -->
    <TextView
        android:id="@+id/text_one"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Nothing yet." />

    <!-- The second item -->
    <TextView
        android:id="@+id/text_two"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Nothing yet." />

    <!-- The third item -->
    <TextView
        android:id="@+id/text_three"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Nothing yet." />
</LinearLayout>

Next, open the MainActivity class and bind the views to fields:

public class MainActivity extends AppCompatActivity {

    private Button button;
    private TextView textOne;
    private TextView textTwo;
    private TextView textThree;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = (Button) findViewById(R.id.button_load_items);

        textOne = (TextView) findViewById(R.id.text_one);
        textTwo = (TextView) findViewById(R.id.text_two);
        textThree = (TextView) findViewById(R.id.text_three);
    }
}

You can launch your app at this point, and you should see a button with three text views under it. Nothing happens when you click the button, though, and we don’t have the MVP in place just yet. Let’s fix that! First, let’s finalize our view layer by making the activity implement the MyView interface:

// Note the 'implements' part
public class MainActivity extends AppCompatActivity implements MyView {

    // The other stuff goes here

    @Override
    public void showItems(List<String> items) {
        // Let's populate the text views
        textOne.setText(items.get(0));
        textTwo.setText(items.get(1));
        textThree.setText(items.get(2));
    }
}

Now just three more things: First, let’s create a new, concrete service class that pretty much does just the same thing as the one we wrote in the test:

public class ConcreteService implements MyService {
    @Override
    public List<String> getItems() {
        List<String> items = new ArrayList<>();
        items.add("A");
        items.add("B");
        items.add("C");
        return items;
    }
}

Second, create the presenter in the activity:

// Other fields go here...

// Here's the presenter
private MyPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Here we pass the activity ('this') as the view and create a new service
        presenter = new MyPresenter(this, new ConcreteService());

        // The other stuff goes here...

And for the grand finale, let’s make the button to actually call the presenter:

button = (Button) findViewById(R.id.button_load_items);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        presenter.load();
    }
});

AND WE ARE DONE! Let’s take the app for a spin. After you launch the app, you should see this:

Before...

And when you click the button, you should see the magic happen:

..and AFTER!

Awesome!

Conclusion

Doing MVP in Android is nothing difficult, just like TDD is not difficult. However, both (and especially TDD) require a certain change in your mindset if you’re not used to them.

With TDD, you end up with highly tested code and you become more confident about it. When you write your code in an MVP manner, your code is well structured and, most importantly, it’s testable. Together, these two provide you with a stable framework to build your app upon.

As always, if you found this post helpful or if you learned something and want to thank me, consider buying me a cup of coffee. It fuels my coding and makes me want to write more posts. :) Until next time!