Insider's Guide To Udacity Android Developer Nanodegree Part 4 - Build it Bigger
Written by Nikos Vaggalis   
Monday, 02 October 2017
Article Index
Insider's Guide To Udacity Android Developer Nanodegree Part 4 - Build it Bigger
Build It Bigger Project
Getting to Endpoints
Putting it all together

 

Putting it all together

That took care of the GCE and the Joke generating library, so let's now have a high level overview of the flow of the application:

Clicking on the main's 'app' screen 'Show a Joke' button, fires an Asynctask which communicates with the GCE. The GCE in turn calls into the Java library joke generator 'jokelib', retrieves the joke and returns it to app which in turn forwards it to the Android library 'showlib', the one that finally displays it:

 

pic20

 

This also goes to highlight the difference between a Java and Android library;the latter has the ability to bundle resources and have a UI as such acting like a self-contained Activity but with the added restriction that it can only be instantiated by another Activity.

 

Flavors

Another requirement specifically introduced in order to make you really dig into Gradle's build configuration was to create two product flavors that group the source code according to functionality.


So in the same project we have a common source base, which further diversifies by code specific to a given flavor, i.e. a paid version which utilizes In-app Billing and a free one which utilizes the Admob Api to display apps.

For Gradle this just required tweaking the productFlavors configuration block:


   productFlavors {
               free {
                      applicationId
"com.example.udacity.declaringflavors.free"
                    }
               paid {
                      applicationId "com.example.udacity.declaringflavors.paid"
                     }
               }
        

 

Now what was left was to write the code for each case, something that Fragments are perfect for since they permit  having a common 'main' code base but differentiate through multiple instances which host the per case code.

 

Testing

We've already utilized testing in the previous modules, specifically local unit testing with Junit and UI instrumentation/ connected/ integrated testing with Espresso.Yes, the terminology is not of much help.

The primary distinction between the two,is that unit tests run on a regular Java VM on your computer. And connected tests run on an Android device or emulator.In general, you should use unit tests for testing generic, non-Android related classes as any such code that calls the android API will fail.

In this case, you should either use a mocking framework like Makido, to mock the appropriate android dependencies, or use a connected test instead.

Nicely settled by the instructors.

The focus here is on the Connected part only, also performed through JUnit, with the difference from Espresso being that we don't test the UI itself but instead isolate non UI related tasks that must be run on an Android Virtual machine, Emulator or Physical Device, and test them against some predefined assertions.

In this case the tests had to "verify that the AsyncTask is indeed loading jokes“ and "that the Async task successfully retrieves a non-empty string”.

The issue here was that by design I had the AsyncTask retrieving the joke by connecting to the GCE and subsequently passing it on to the Android library (showlib) through its PostExecute method.


@Override

    protected void onPostExecute(String result) {
        final Intent intent =

        new Intent(context, ShowActivity.class);     
       
        intent.putExtra("gce_result",result);

        context.startActivity(intent);
    }
    

This was hindering testing as the AsyncTask couldn't be isolated and checked against the test's requirements since the returned joke would had been only accessible from within PostExecute calling the 'showlib' Activity as such tying AsyncTask to the UI. This would require writing tests against the UI too (with Espresso) which would defy JUnit's usage in the first place.

Alternatively, I could had modified the code so that again inside Postexecute, would instead use an Interface that would play the role of communicating the joke from the AsyncTask back to the app's main Activity, that way also delegating calling 'showlib' to it. This would avoid calling 'showlib' inside PostExecute, as such AsyncTask's could now run in isolation and independent of the UI.

But since I wasn't in the mood to disturb the balance already established, and as this was a class on Gradle after all, I used Gradle's flexible configuration to my advantage. So I just made a new AsyncTask that doesn't use PostExecute which would consume the retrieved joke, but instead returns it to its caller. This technique, while not appropriate for returning a joke back to the UI thread was sufficient for a standalone test and on its own thread.

The test related code could then be run in its own isolated thread container:

 

package com.udacity.gradle.builditbigger;

import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import java.util.concurrent.TimeUnit;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class EndpointsAsyncTaskTestUnit {
    @Test
        public void iTest() throws Exception {
            EndpointsAsyncTaskTest aTest =  new EndpointsAsyncTaskTest();
            aTest.execute(InstrumentationRegistry.getContext());
            String joke = aTest.get(5, TimeUnit.SECONDS);
            Assert.assertEquals(5,joke.length());
        }
}

 

The test simply checks that the length of the string/joke returned is in fact 5, as the jokes available 'joke1','joke2', and so on are all of them of length 5.  

After writing the test code I directed Gradle to initialize the new EndpointsAsyncTaskTest (aTest) the one without the PostExecute method, from the custom directory 'src/instruTest/java'
when doing Connected tests:


 sourceSets {
            androidTest{
                java.srcDir file('src/instruTest/java')
            }
        }

This made the test pass and placed the final element in the project.

Closing up

Closing up, I found "Gradle for Android and Java" a pleasant intermission in the Nanodegree, one that included a challenging project that required merging quite a few diverse concepts. There's a few other things that I would have liked to see included in the class, like the difference between the CompileSdk and TargetSdk configuration options; Gradle's Runtime vs Compile time scope of dependencies and especially more material on Connected testing, but nevertheless I have learned a lot, once again.

 

More Information

Android Developer Nanodegree

Nikos Vaggalis - "Build it Bigger" on GitHub

Related Articles

Insider's Guide To Udacity Android Developer Nanodegree - 1

Insider's Guide To Udacity Android Developer Nanodegree - 2

Insider's Guide To Udacity Android Developer Nanodegree - 3

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

 

Banner

raspberry pi books

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Monday, 20 November 2017 )