Over a million developers have joined DZone.

Android Clean Code:  Part 3

DZone's Guide to

Android Clean Code:  Part 3

Learn more about the Clean Code Android mobile design pattern and unit testing your Android app piece by piece for code quality.

· Mobile Zone ·
Free Resource

Having understood how to test the Android Activity and Interactor in Part 1 and Part 2 of this series, let’s look at the Presenter and its testing.

What Is the Work of Presenter?

A) To change the data retrieved from Interactor according to the UI needs. Typically, you may want to decorate the data or derive a value from the set of data fetched by Interactor (for example, the total number of future trips in the list of the trips that contains both future and past trips).

B) Filter data, when needed.

C) Sort the data (default sorting), when needed

In this example, we use Presenter for decoration.

The date of travel is in the YYYY/MM/DD format when passed from Interactor; we need to show a message showing the days until departure if the trip is in the future.

Image title

If it is a past trip, it will show a message with the days since the departure.

Image title

Let’s look at the Presenter:

interface HomePresenterInput {
    public void presentHomeMetaData(HomeResponse response);

public class HomePresenter implements HomePresenterInput {

    public static String TAG = HomePresenter.class.getSimpleName();

    public WeakReference<HomeActivityInput> output;
    private Calendar currentTime;

    public void presentHomeMetaData(HomeResponse response) {
        // Log.e(TAG, "presentHomeMetaData() called with: response = [" + response + "]");
        //Do your decoration or filtering here
        HomeViewModel homeViewModel = new HomeViewModel();
        homeViewModel.listOfFlights = new ArrayList<>();

        if (response.listOfFlights != null) {
            // create a viewmodel out of model
            for (FlightModel fm : response.listOfFlights) {
                FlightViewModel fvm = new FlightViewModel();
                fvm.checkInStatus = fm.checkInStatus;
                fvm.terminal = fm.terminal;
                fvm.gate = fm.gate;
                fvm.flightName = fm.flightName;
                fvm.startingTime = fm.startingTime;
                Calendar startingTime = getStartingTimeCalendar(fvm);
                long daysDiff = getDaysDiff(getCurrentTime().getTimeInMillis(),startingTime.getTimeInMillis());
                setDaysFlyDecorationText(fvm, daysDiff);

    private void setDaysFlyDecorationText(FlightViewModel fvm, long daysDiff) {
        if(daysDiff >=0){
            fvm.noOfDaysToFly = "You have " + daysDiff + " days to fly";
        } else {
            daysDiff =-daysDiff;
            fvm.noOfDaysToFly = "It has been " + daysDiff + " days since you flew";

    private Calendar getStartingTimeCalendar(FlightViewModel fvm) {
        int year = Integer.parseInt(fvm.startingTime.substring(0,4));
        int month = Integer.parseInt(fvm.startingTime.substring(5,7));
        int day = Integer.parseInt(fvm.startingTime.substring(8,10));
        Calendar startingTime = Calendar.getInstance();
        return startingTime;

    private long getDaysDiff(long startTime,long endTime) {
        long msDiff = endTime - startTime;
        long daysDiff = TimeUnit.MILLISECONDS.toDays(msDiff);
        Log.e(TAG,"diff is  "+ daysDiff);
        return daysDiff;

    public Calendar getCurrentTime() {
        if(currentTime == null) return Calendar.getInstance();
        return currentTime;

    public void setCurrentTime(Calendar currentTime) {
        this.currentTime = currentTime;

Presenter implements the PresenterInput interface. In this class, the methodpresentHomeMetaData takes care of data decoration. It creates the view model from the model that is passed by the Interactor and passes it to the Activity to display the data. Most of the data is copied without any change to the view model from the model, except noOfDaysToFly, where the private methodsetDaysFlyDecorationText does the calculation of the days to fly, based on the current date.

Once the view model is created, Presenter calls the Activity using the ActivityInput Interface.

The class member output of type ActivityInput is a  WeakReference  so that we don’t create circular references. These members were wired by Configurator which will be explained in a future post.

android-clean-code-generator generates these classes, which will be explained in a future post.

If you look at the code again, you will be able to understand the logic; it is straight forward.

Unit Testing

Let’s look at unit testing the code. We will start with the test to verify whether Activity is called with the right inputs. Should we need Activity class to test the presenter?

No, we should use the mock of Activity. Let us create the Mock first:

private class HomeActivityInputSpy implements HomeActivityInput {
        public boolean isdisplayHomeMetaDataCalled = false;
        public HomeViewModel homeViewModelCopy;
        public void displayHomeMetaData(HomeViewModel homeViewModel) {
            isdisplayHomeMetaDataCalled = true;
            homeViewModelCopy = homeViewModel;

Let us create a test method to verify if the displayHomeMetaData is called with the right inputs:

    public void presentHomeMetaData_with_vaildInput_shouldCall_displayHomeMetaData(){
        HomePresenter homePresenter = new HomePresenter();
        HomeResponse homeResponse = new HomeResponse();
        homeResponse.listOfFlights = new FlightWorker().getFutureFlights();

        HomeActivityInputSpy homeActivityInputSpy = new HomeActivityInputSpy();
        homePresenter.output = new WeakReference<HomeActivityInput>(homeActivityInputSpy);


        Assert.assertTrue("When the valid input is passed to HomePresenter Then displayHomeMetaData should be called", 

Now knowing that the next class in the unidirectional data flow is called as expected, let us test the decoration logic — present the number of days to fly instead of the date of travel.

One of the  FIRST principles in writing successful test cases is that the test should be repeatable.

The value for `viewModel.noOfDaysToFly` is calculated based on the current date, the value for `viewModel.noOfDaysToFly` can change depending upon the time of unit test execution. We overcome that by setting the current time as (2017-May-30) in the test code and make the test performing consistently. We use the object ActivityInputSpy to verify the values of the view model.

    public void verify_HomePresenter_getDaysDiff_is_CalcualtedCorrectly_ForFutureTrips(){
        HomePresenter homePresenter = new HomePresenter();
        HomeResponse homeResponse = new HomeResponse();
        ArrayList<FlightModel> flightsList = new ArrayList<>();
        FlightModel flight1 = new FlightModel();
        flight1.startingTime = "2017/12/31";
        homeResponse.listOfFlights = flightsList;

        HomeActivityInputSpy homeActivityInputSpy = new HomeActivityInputSpy();
        homePresenter.output = new WeakReference<HomeActivityInput>(homeActivityInputSpy);

        //When - current time is set to 2017-May-30
        Calendar currentTime = Calendar.getInstance();

        // "It has been " + daysDiff + " days since you flew";
        String ExpectedText = "You have " + "184" + " days to fly";
        String ActualText = homeActivityInputSpy.homeViewModelCopy
        Assert.assertEquals("When current date is 2016/10/12 & Flying Date is 2016/10/31" 
                            +"Then no of days should be 184",


Like the above example, we can test each and every public method of the presenter. By this time, you may have realized how powerful the Android Clean Code design pattern is- it helps you unit test the app piece by piece, without hardwiring dependencies. Check the complete Presenter test class here.

I suggest you clone the example project, if not done already and before we close this exercise, run the test cases with code coverage(Android Studio → Menu →Run →Run with coverage). Check the code coverage of the Presenter class.

What’s next? We’ll talk about Router in the next post.

clean code ,mobile app testing ,clean architecture ,unit testing ,mobile testing ,mobile design

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}