Thursday, May 28, 2015

Android TDD Series: The Setup

Previously I spoke about the challenges in following Test-Driven Development in Android.  Armed with the fore-knowledge of what we're getting ourselves into we're now ready to dive into the first serious attempt at test-driving some functionality in.

The App


To make this series a little more light-hearted I'm going to develop an app I'll call the "Business Unit Estimator".  This app will let the user take a picture of someone or someplace and assign how many "Units of Business" can be accomplished by that person or place.  For example, take the gentleman in the meme image below:

Via Quickmeme

This fellow is wearing a tie (+5 units of business right there), as well as a business jacket (+3 units of business), his hair is immaculate (+1 units of business), and he's on a cell phone (+2 units of business, as obviously more work gets done when you're on a phone).  That's a grand total of 11 units of business.  This guy means business!  Silliness aside this will provide an app we can work on to demonstrate TDD in Android.



Pre-Reqs

I'm going to make a number of assumptions before we get going.  First, I'm going to assume you have the following installed on your system:

  • Android Studio 1.1+
  • The latest Android SDK (the previous link will get this for you as well)
  • Java 7 (note: we don't want 8 here, as Android is not compatible with 8)
  • Gradle (preferably installed via GVM)
I'm also going to assume that you know how to create an Android project in Android Studio via the usual File -> New -> New Project with a "Blank Activity".  If you'd like a shortcut on creating the project feel free to check out the demo app on my Github page.

Setup

This entry in the series will focus on your environment setup to allow Test-Driving to occur via unit tests and Robolectric.

Robolectric is an Android unit testing framework that allows you to write tests which will run on the JVM, rather than on the emulator.  This has several benefits.  First, it will greatly shorten your feedback loop, as tests which run on the JVM run extremely fast.  Compare this with the out-of-the-box tools provided by Google which by and large require you to deploy code to emulator, wait for it to load, and finally wait for the test to run and you'll see a huge difference.  A secondary benefit here is that running on the JVM allows you to use a mocking framework such as Mockito to control the behavior of your dependencies.

Throughout this series I'll be using Robolectric 3.  Let's add the dependencies we need in our app/build.gradle file:


dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.1.1'

    testCompile 'org.hamcrest:hamcrest-integration:1.3'
    testCompile 'org.hamcrest:hamcrest-core:1.3'
    testCompile 'org.hamcrest:hamcrest-library:1.3'
    testCompile 'junit:junit:4.12'
    testCompile 'org.mockito:mockito-core:1.+'
    testCompile 'org.robolectric:robolectric:3.0-SNAPSHOT'
    testCompile 'org.robolectric:shadows-support-v4:3.0-SNAPSHOT'
}

This block should pull in everything we need to write unit tests.  While we won't be using Mockito just yet, we'll need it soon enough.  The shadows-support dependency will provide additional support for accessing parts of the Android SDK in the test environment.  We're also going to use the 3.0-SNAPSHOT versions here as Robolectric 3 is still in RC at the moment; fear not, the API solid despite that.

Next we need to add the app/src/test/java folder in our project structure, as it is not added for us when the project is generated:

Adding the app/src/test/java folder.

Our First Test

Once this is completed we can add our first test:


package com.jameskbride;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowActivity;

import static org.junit.Assert.assertEquals;

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class)
public class MainActivityTest {

    private MainActivity activity;

    @Before
    public void setUp() {
        activity = Robolectric.setupActivity(MainActivity.class);
    }

    @Test
    public void whenTheActivityIsCreatedThenTheContentViewShouldBeSet() {
        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
        assertEquals(R.id.main, shadowActivity.getContentView().getId());
    }
}

There is a lot going on here which we're going to cover in more depth later on, but for now you need to know that the @RunWith and @Config annotations are required to run the Robolectric test. You should also be aware that there are multiple versions of BuildConfig.class, and you'll need to use the one which is generated for your project and not the android.support.v4 or android.support.v7.appcompat versions. Using these versions will cause errors. I'm going to skip over the setup for the moment (we'll cover this in the next entry in the series) and jump straight to the test. When we generated the project a MainActivity class was generated for us under app/src/main/java.

package com.jameskbride;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;


public class MainActivity extends AppCompatActivity {

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

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
Unfortunately (from a TDD perspective) it also added some logic to set the content view. Our first test is going to add coverage for this functionality.
    @Test
    public void whenTheActivityIsCreatedThenTheContentViewShouldBeSet() {
        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
        assertEquals(R.id.main, shadowActivity.getContentView().getId());
    }
As you can see we are using a ShadowActivity, and asserting that the content view ID has been set.  Let's execute the test. From the root of our project we'll run:
./gradlew testDebug

This causes a compilation error, as the id for main doesn't exist yet.
/home/jim/projects/BusinessUnitEstimator/app/src/test/java/com/jameskbride/MainActivityTest.java:28: error: cannot find symbol
        assertEquals(R.id.main, shadowActivity.getContentView().getId());
                         ^
  symbol:   variable main
  location: class id
1 error
:app:compileDebugUnitTestJava FAILED
Let's make this test pass by adding the id field which will allow us to verify that it is set as the content view.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"
    android:id="@+id/main">

    <TextView android:text="@string/hello_world" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

The test is passing now and we've successfully demonstrated how to setup and run a Robolectric test. If you'd like to get hands-on with the example code at this point you can check out out from Github.  Join me next time when we'll go more into depth on test-driving Activities.  Also, I'm always looking for feedback, so please leave comments.  Thank you!

Tuesday, May 19, 2015

Code, Creativity, and Beauty

Earlier today I was teaching a class on scrum and XP practices.  During the course of a conversation on Test-Driven Development I made a statement to the effect of “I want to let my tests drive out my design, and not the other way around”.  One of the individuals in the class protested against this. Not against TDD mind you, but against the idea of simply following the narrow path and narrow design that the test was going to cause.  His position was that this method pays no heed to the “creative nature” of software development.  There was further conversation regarding the nature and implementation of TDD that isn’t pertinent here, but his statement stuck with me.

Is there an inherent creativity to software development?

This thought stayed with me for the rest of the day, and as I was washing the dishes just minutes ago I had a sudden dialog in my head on the subject.  You might say (ironically) that my muse visited me, and I was inspired to do something creative.  I rushed to finish the dishes and feverishly started writing out these thoughts before I lost them.

In my daily practice I do not typically stop to consider if, while test-driving a feature, what I’m doing is creative in nature.  I’m building useful things.  These things are going to be employed by someone for a purpose.  Their nature is that of tools, albeit sophisticated tools which are capable of great feats. I know what the thing should do, and I’m writing test code in a way to force the behavior of the code.  Is that creative?  Obviously the simple act of putting syntax to electrons is creative in the sense that you literally just created form and structure where none were before, just as putting these words together for you to read is a creative act.  The question I’m asking is whether there is anything inherently creative (aside from the act itself) in writing code beyond fulfilling a purpose?

Another thought: Can code be considered “beautiful”?

The individual in question also made a comparison of code to music score which I honestly can’t remember now.  I wish I could, but again, this got me thinking.  From my point of view, music and code come from two very different worlds.  Music is creative and beautiful and simply is.  It has not been written to serve a purpose beyond mere entertainment, while software development in general and code in particular was very specifically created to be utilitarian.

There is certainly code which is written purely to be creative and beautiful.  One need search no further than Perl Poetry for examples.  However, ask the average developer if the code that they are working on is beautiful and see how they respond.  They may surprise me, but I think my chances are more than just good that they would describe what they were doing as creating tools to serve a purpose, and beauty and creativity be damned.

Having said this I certainly don’t believe that the act of creating something is in any way at odds with imbuing it utility and purpose.  As a software developer I’ve seen (and endeavored to create, successfully or not) code which is elegant in its simplicity and utilitarian in its application.

Supposing code can be beautiful, how it its beauty derived?  Is is beautiful because it serves a purpose?  Or can it be beautiful simply in its own right?  Can a shovel be called beautiful?  After all, code and shovels are tools which both serve a purpose, though the shovel is infinitely more simple (elegant?).  There is the old saying that beauty is in the eye of the beholder, and that may be so.  If it is, then all code, regardless of its function (or non-functioning nature as the case may be!), could potentially be called beautiful.  This is a concept that is beyond my normal thought processes.  If a thing created for a purpose fails in its purpose can it still be considered beautiful?  If I can’t use the shovel to move material from one location to another can it, as a tool, still be considered beautiful?

To use a metaphor which is probably more familiar to most software developers, can a brick in a cathedral be considered to be beautiful?  Yes, the brick might be used to build a cathedral, and I believe it is a safe thing to say that most people consider cathedrals to be quite beautiful, but is the brick itself beautiful? To take the metaphor further people often point to cathedrals in awe, stating how incredibly beautiful and inspiring they are.  They point to elegant arches, flying buttresses, and columns, and remark at the feelings they invoke in the onlooker.  In deed, part of the purpose (see what I did there?) of cathedrals is to cause a sense of awe and grandeur in worshippers.  The church and its architects, purposely designed it that way.  They also, by the way, designed the arches, flying buttresses, and columns for specific purposes as well.  All of them in their own way support the system of the cathedral, that is they keep the building from collapsing in on itself.  Can they be considered beautiful?  Certainly, as history and wide opinion shows us. However, at the same time they are far more *useful* in purpose than many people realize.  Were they created to be intentionally beautiful while at the same time serving important purposes, or were they simply created to fulfill a purpose?

As software developers is the act of writing software inherently creative, and is the code inherently beautiful, or is it simply the act of building a tool for a utilitarian purpose?

Thursday, May 14, 2015

Android TDD Series: The Challenges

Six months ago I embarked on a journey to write my first Android app.  I'm still on that journey, and though I haven't released yet (soon!) I've learned a number of hard lessons that I'd like to share with those who are on the same path in the hopes that it will make their own journey a little easier.

Before I get to those lessons though I want to talk about some of the challenges I faced when I first approached Android development. First among them is the fact that...


You're Developing Against A Moving Target

Mobile is hot right now, and Android is constantly being updated to keep pace.  When I first started working on the my app KitKat was the latest and greatest.  Lollipop was released a month later, and at the time of this writing it already has close to 10% of the Android market share.  The build tools and support libraries are also constantly being updated, as well as the Gradle plugin and the most popular Android IDE, Android Studio.  As such, the API and your development environment can shift quickly, and library developers have to scramble to keep up.  Information that was accurate a couple of months ago can now be completely out of date.  Libraries that were compatible last week may suddenly stop working.  It's a fast-paced environment, and if you're going to be an Android developer you should be prepared to keep up.  This means following the latest Android news, keeping an eye on third-party tools, and being ready to update on a moment's notice.

As if hitting a moving target wasn't hard enough....



The Framework Is Not Designed For Maintainability


Android was written by people who were attempting to anticipate the varied needs of future app developers, and it's quite obvious that some needs were prioritized over others.  It's also quite obvious that these people (naturally) brought their own biases and schools of thought into the project.  As such, things like testable, maintainable designs and the application of well-understood principles such as Dependency Injection took a backseat to performance.

Reading the Android documentation on managing your memory usage is like reading a diatribe against every good practice you've ever used.  Thinking of decomposing your code into smaller, more focused, more maintainable classes?  Don't do that, think of the overhead!  Want to pull in a known library that meets your needs?  Heavens no, who knows what that library is doing. You should just write your own!  Google even goes so far as to suggest in their documentation that you shouldn't design apps to use the aspects of the framework they themselves provided.  All of these things we do to make our code easier to maintain and test (much less useful!) are flat-out discouraged in the official docs and maddeningly apparent in the implementation.

Admittedly these are some easy potshots to take at Google's expense, and they certainly bring up some interesting data points in regards to their design recommendations.  However, I'm of the opinion that the state of the art for Android has advanced far beyond the point that these recommendations make any sense any more.  This is true in just about every respect; the hardware has improved in leaps and bounds in the space of just a few short years, the ecosystem around Android has grown tremendously, the tooling has been steadily improving, and a thriving community provides an endless number of useful libraries.

Yes, we should eat our vegetables, brush our teeth, and keep performance in mind, but this in no way means we should simply roll over and forget all of the lessons we learned outside of mobile arena.  Gone are the days in which it was acceptable to write monster methods simply to save a few bytes instead of writing new classes and methods.  Gone are the days in which it was acceptable to new-up every dependency inline and call it good enough.   These days there is simply no excuse to not apply the same level of craftsmanship to Android applications as we would to any other project, which leads me to my next point...


TDD Is Hard (But Not Impossible)

There's no two ways about it, Test-Driven Development in Android is daunting.  Coupling the issues with maintainability I mentioned above with the fact that out of the box there are little to no testing tools available makes getting started with TDD a difficult proposition.  Luckily, the Android community has risen to this challenge, and Google has responded to the need for test support with updates to the build tools and other improvements.  Testing tools such as Robolectric used in conjunction with dependency injection frameworks such as Dagger and a healthy dose of mocks (via Mockito or similar) go a long way towards making testing a much less frustrating experience. Functional testing is now simplified as well, as frameworks such as Espresso and Robotium provide easier and more reliable UI interactions than the standard ActivityInstrumentationTestCase2 style tests.  

Even with these tools though testing is still not easy if you're not careful to design your app to be testable.  It is all too easy to simply succumb to the framework if you don't take the time to become intimately familiar with its ins and outs, and to learn when you can and should ignore a design recommendation.  Inline, anonymous listener classes?  As Dikembe Mutombo would say, "Not in my house!"  Business logic embedded in a framework class?  Nope!  It takes diligence and a critical eye for every design decision if you're going to test-drive your code in Android.  Be ready to go off the beaten path.  By the way....


If the Path Doesn't Exist, You Might Have to Build It

As I said earlier, Android is a moving target, and library developers have to scramble to keep up.  Often the pace of advancement is quick enough that the support you're looking for might simply not be there yet.  I've already had to contribute to more than one project in order to remove roadblocks for development, and chances are good that if you're reading this as an aspiring Android developer you might have to as well.  If you're using Robolectric be ready to extend Shadow classes when it becomes necessary.  Also, don't be afraid of cloning a project and pulling down the source to research a bug in your third party libraries, or of figuring out a fix to contribute back. 


These are just a few of the challenges I've encountered while developing my first Android app, and I've outlined them here to give other developers an idea of what to expect when they enter this arena. Over the course of the next several months I'll be writing an Android TDD Series, in which I'll try to provide concrete examples of how to test-drive various pieces of functionality in an example app.  I hope you'll join me here, and I look forward to your feedback.  Thanks and happy app dev'ing!