Testing android MVP

What this post is about?

  • Using Gradle and Android Studio to run Android tests
  • Writing unit tests for Android using JUnit4
  • Writing UI tests with Espresso and the Android Testing Support Library
  • We will be using our last project so if you are new please check out Dagger 2 and MVP Architecture  first

Let’s get started

Step 1: Add the necessary dependency

The Android Testing Support library (ATSL) provides a great framework for testing your Android app. It has a JUnit 4-compatible test runner (AndroidJUnitRunner) and functional UI testing through Espresso and UI Automator. You can run tests for these APIs directly in the Android Studio IDE or from the command line, which makes it very easy to integrate them into your development flow.

 

// Dependencies for local unit tests
testCompile "junit:junit:4.12"
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestCompile 'com.android.support:support-annotations:24.1.1'
compile 'com.android.support.test.espresso:espresso-idling-resource:2.2.2'
compile 'com.android.support.test.espresso:espresso-contrib:2.2.2'

 

Add this

android {
    ...
    ...
    ...
    ...
    ...
 
    testOptions.unitTests.all {
        testLogging {
            events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
        }
    }

    packagingOptions {
        exclude 'META-INF/maven/com.google.dagger/dagger/pom.properties'
        exclude 'META-INF/maven/com.google.dagger/dagger/pom.xml'
    }


}

And add this line in the defaultConfig  block

defaultConfig {
    ....

    testInstrumentationRunner ""
}

If you perform a sync there will be a conflict with the minSdk version for test source set and main source set, so create AndroidManifest.xml  in  AndroidTest source set

 2016-08-18_00h10_01.png

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="${applicationId}">
    <uses-sdk tools:overrideLibrary="android.app, android.support.test, android.support.test.rule, android.support.test.espresso, android.support.test.espresso.idling, android.support.test.uiautomator.v18"/>
</manifest>

Step 2: Create an IdlingResource file to idle resource when the application makes call to Network

 

public class EspressoIdlingResource {

    private static final String RESOURCE = "GLOBAL";

    private static CountingIdlingResource mCountingIdlingResource =
            new CountingIdlingResource(RESOURCE);

    public static void increment() {
        mCountingIdlingResource.increment();
    }

    public static void decrement() {
        mCountingIdlingResource.decrement();
    }

    public static IdlingResource getIdlingResource() {
        return mCountingIdlingResource;
    }
}

Step 3: Add the following static method in MainActivity.java

@VisibleForTesting
public IdlingResource getCountingIdlingResource() {
    return EspressoIdlingResource.getIdlingResource();
}

Step 4: Add Idling status when network request is made

The CountingIdlingResource is incremented when we make a NetworkRequest and Decrement after the network request finishes.

Note:. When the counter is 0 – it is considered to be idle, when it is non-zero it is not idle.

So in onCreate we increment the counter before calling the loadPost in our Presenter

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    listView = (ListView) findViewById(R.id.my_list);
    list = new ArrayList<>();

    DaggerMainScreenComponent.builder()
            .netComponent(((App) getApplicationContext()).getNetComponent())
            .mainScreenModule(new MainScreenModule(this))
            .build().inject(this);


    //Increment the counter before making a network request
    EspressoIdlingResource.increment();

    //Call the method in MainPresenter to make Network Request
    mainPresenter.loadPost();
}

Similarly,  we decrement the counter after the posts are fetched

@Override
public void showPosts(List<Post> posts) {
    //Loop through the posts and get the title of the post and add it to our list object
    for (int i = 0; i < posts.size(); i++) {
        list.add(posts.get(i).getTitle());
    }
    //Create the array adapter and set it to list view
    adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
    listView.setAdapter(adapter);
    //Decrement after loading the posts
    EspressoIdlingResource.decrement();
}

Also, decrement the counter if there is an error (No active Internet or Server error)

@Override
public void showError(String message) {
    //Show error message Toast
    Toast.makeText(getApplicationContext(), "Error" + message, Toast.LENGTH_SHORT).show();
    
    // If there is no network connection we get an error and decrement the counter because the call has finished
    EspressoIdlingResource.decrement();
}

Step 5: Write the test case

Create a file MainActivityTest.java in AndroidTest/java source set

@RunWith(AndroidJUnit4.class)
@SmallTest
public class MainActivityTest {

    @Rule
    public ActivityTestRule<MainActivity> mainActivityActivityTestRule = new ActivityTestRule<>(MainActivity.class);


    @Test
    public void checkRecyclerView() throws Exception {
        Espresso.registerIdlingResources(mainActivityActivityTestRule.getActivity().getCountingIdlingResource());
        Espresso.onView(ViewMatchers.withId(R.id.my_list)).perform(ViewActions.click());
    }
}

Step 6: Run the Test by creating a new Run Configuration

2016-08-18_00h28_47.png

Then click on the plus icon and select Android Tests

2016-08-18_00h30_26.png

Configure the test as follows
Name : Test (Can be anything)

Module : app

Specific instrumentation runner : android.support.test.runner.AndroidJUnitRunner

and then click Ok

2016-08-18_00h31_16.png

Now, select the newly create Run Configuration for the Run Menu

2016-08-18_00h31_16.png

Select the device to run the Instrumentation test

2016-08-18_00h42_55.png

2016-08-18_00h43_06.png

2016-08-18_00h43_49.png

Finally, you should see the Test pass. If the test takes too long to finish stop and rerun.

Conclusion

  • We learnt to set up Android Testing Support library
  • We used Espresso’s Idling resources feature to wait till the network call happen
  • We created a custom run configuration to run our tests

Github’s link for code

https://github.com/LadwaAditya/DaggerMVP-Tutorial

Reference

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s