Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Android Clean Code :  Part 2

DZone's Guide to

Android Clean Code :  Part 2

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

Free Resource

Get gorgeous, multi-touch charts for your iOS application with just a few lines of code.

Having understood how to test the Android Activity in part 1 of the series, let’s look at Android Clean Code's Interactor and its unit testing.

What Is the Work of the Interactor?

Interactor retrieves the data from data sources like the local DB, web service, and content provider. In this example, Interactor is expected to fetch the list of present or future flight trips. Let’s look at the Interactor class:

interface HomeInteractorInput {
    public void fetchHomeMetaData(HomeRequest request);
}

public class HomeInteractor implements HomeInteractorInput{

    public HomePresenterInput output;

    public FlightWorkerInput flightWorkerInput;


    public FlightWorkerInput getFlightWorkerInput() {
        if (flightWorkerInput == null) return new FlightWorker();
        return flightWorkerInput;
    }

    public void setFlightWorkerInput(FlightWorkerInput flightWorkerInput) {
        this.flightWorkerInput = flightWorkerInput;
    }

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

    @Override
    public void fetchHomeMetaData(HomeRequest request) {
        flightWorkerInput = getFlightWorkerInput();
        HomeResponse homeResponse = new HomeResponse();
        if(request.isFutureTrips == true) {
            homeResponse.listOfFlights = flightWorkerInput.getFutureFlights();
        } else {
            homeResponse.listOfFlights = flightWorkerInput.getPastFlights();
        }

        Log.e(TAG,"In method fetchHomeMetaData");
        output.presentHomeMetaData(homeResponse);
    }
}

The Interactor implements the InteractorInput interface defined in part 1. In the HomeInteractor class, the fetchHomeMetaData method takes care of data retrieval and, based on certain logic (whether the trip is the past or future trip), delegates the data retrieval work to another class called FlightWorker.

How Does Interactor Talk to Worker? 

It uses the interface WorkerInput.

Image title

Why Do We Use Interfaces?

Loose coupling- classes talk to each other's methods using the interfaces, so during the unit testing, we will be able to mock the other dependent classes without much of a sweat.

What Is the Need for the Worker?

  • If the task is simpler, you can have all the work done by the Interactor itself.
  • If you foresee that the task can be reused somewhere else in the app, move it to the Worker.Image title
Interactor calling multiple workers.

When Interactor needs to perform more than one task, having multiple workers to execute each one of the tasks is the recommendation of clean architecture — confirming the SOLID principle.

Unit Testing

Let’s test whether the correct Worker method has been invoked. How do we test this scenario? 

We don’t need a real Worker; let’s create a spy.

private class FlightWorkerInputSpy implements FlightWorkerInput {

        boolean isgetFutureFlightsMethodCalled = false;
        boolean isgetPastFlightsMethodCalled = false;

        @Override
        public ArrayList<FlightModel> getFutureFlights() {
            isgetFutureFlightsMethodCalled = true;
            ArrayList<FlightModel> flightsList = getFlightModels();
            return flightsList;
        }

        @Override
        public ArrayList<FlightModel> getPastFlights() {
            isgetPastFlightsMethodCalled = true;
            ArrayList<FlightModel> flightsList = getFlightModels();
            return flightsList;
        }
    }

Note that you cannot test Interactor without Presenter instance and we don’t want the real instance of Presenter either. Let’s create a spy for Presenter and use it when needed.

private class HomePresenterInputSpy implements HomePresenterInput {

        boolean presentHomeMetaDataIsCalled = false;
        HomeResponse homeResponseCopy;
        @Override
        public void presentHomeMetaData(HomeResponse response) {
            presentHomeMetaDataIsCalled = true;
            homeResponseCopy = response;
        }
    }

With the test setup work complete, let's write a test method to check if the worker is called with the right inputs.

@Test
    public void fetchHomeMetaData_with_vaildInput_FutureTrip_shouldCall_Worker_getFutureTrips(){
        //Given
        HomeInteractor homeInteractor = new HomeInteractor();
        HomeRequest homeRequest = new HomeRequest();
        homeRequest.isFutureTrips = true;

        //Setup TestDoubles
        HomePresenterInputSpy homePresenterInputSpy = new HomePresenterInputSpy();
        homeInteractor.output = homePresenterInputSpy;
        FlightWorkerInputSpy flightWorkerInputSpy = new FlightWorkerInputSpy();
        homeInteractor.setFlightWorkerInput(flightWorkerInputSpy);

        //When
        homeInteractor.fetchHomeMetaData(homeRequest);

        //Then
        Assert.assertTrue("When the input is passed to HomeInteractor is FutureTrip" +
                "Then getFutureFlights should be called in Worker",
                flightWorkerInputSpy.isgetFlightsMethodCalled);
    }

Next, with the Worker unit tested, let’s write the code to test whether the presenter method is called with the right values.

@Test
    public void fetchHomeMetaData_with_vaildInput_shouldCall_presentHomeMetaData(){
        //Given
        HomeInteractor homeInteractor = new HomeInteractor();
        HomeRequest homeRequest = new HomeRequest();
        homeRequest.isFutureTrips = true;
        HomePresenterInputSpy homePresenterInputSpy = new HomePresenterInputSpy();
        homeInteractor.output = homePresenterInputSpy;
        //When
        homeInteractor.fetchHomeMetaData(homeRequest);

        //Then
        Assert.assertTrue("When the valid input is passed to HomeInteractor " +
                        "Then presentHomeMetaData should be called",
                 homePresenterInputSpy.presentHomeMetaDataIsCalled);
    }

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. You can test whether the method is called and also if the parameters have the right values and you can repeat this drill in every public method of the class.

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 Interactor class.

If you are already started worrying about the boilerplate code of this pattern, do not worry, we have code scaffolding done for you from Android Studio, will be explained in one of the future posts.

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

.Net developers: use Highcharts, the industry's leading interactive charting library, without writing a single line of JavaScript.

Topics:
clean code ,unit testing ,mobile app development ,mobile testing ,mobile

Published at DZone with permission of Mohanraj Karatadipalayam. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

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

{{ parent.tldr }}

{{ parent.urlSource.name }}