Saturday, September 19, 2015

Android TDD Series: Test-Driving Views Part 2 - Fragments

Well, it's been more than two months since my last post in the series and I've really fallen off of this horse. The good news is that time away from the Android TDD series was well spent. I have just published my first Android app! Now it's time to get back on and get back to some Android TDD!

We started talking about testing views in Android, specifically how to test activities. While activities are certainly a core component, we actually want to minimize how much code we put in them. This is especially true for all view-related code, such as widget-population and listener functionality. Instead, a majority of this functionality should be placed in fragments. Fragments were introduced into the Android framework when it became apparent that activities would be become bloated and difficult the maintain, with a mishmash of life-cycle management, data population, and widget management. By moving all widget-based functionality (including listeners) we are able to more cleanly delineate the responsibilities for each class. Activities should focus on their life-cycle events and populating the view-model which will be used by the fragment, which will be responsible for managing the widgets and their listeners.

To Test Fragments...


You actually need activities.  This is unfortunate because when we unit test we strive to test classes in isolation as much as possible.  That being said it isn't too onerous to unit test fragments; we simply need an activity when we start the fragment.

For now, let's take a look at how to start a fragment test (src/test/java/com/jameskbride/TextFragmentTest.java):


package com.jameskbride;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.widget.TextView;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.util.ActivityController;

import static org.junit.Assert.assertEquals;

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

    private ActivityController activityController;
    private MainActivity activity;

    @Before
    public void setUp() {
        activityController = Robolectric.buildActivity(MainActivity.class);
        activity = activityController.create().start().visible().get();
    }

    @After
    public void tearDown() {
        activityController.pause().stop().destroy();
    }

    public void startFragment(FragmentActivity parentActivity, Fragment fragment) {
        FragmentManager fragmentManager = parentActivity.getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add(fragment, null);
        fragmentTransaction.commit();
    }

    @Test
    public void whenTheFragmentViewIsCreatedThenTheViewShouldBePopulated() {
        TextFragment textFragment = TextFragment.newInstance();
        startFragment(activity, textFragment);

        TextView myTextView = (TextView)textFragment.getView().findViewById(R.id.my_text_view);
        assertEquals("Hello world!", myTextView.getText());
    }
}

This code is obviously simplified from what you would normally see.  The first thing we do is in the setUp() method, which is to build an Activity and get it into the correct life-cycle event (see my previous post).  Once we are into the actual test itself we have another required step: starting the Fragment.  We accomplish this by getting ahold of the FragmentManager (in this case we're using the SupportFragmentManager, which is actually recommended).  Once we have the FragmentManager we start a new FragmentTransaction and commit that transaction.

Now we have performed enough setup to perform the actual test.  In this case we are simply checking that the text in a TextView widget has been set when the Fragment is created. After we add enough code to get it to compile (create the TextFragment, add the factory method, newInstance(), and create a view which contains an id of "my_text_view" we can run the test via "./gradlew testDebug". This leaves with a failing test which is expecting "Hello world!". Let's get this test passing.

First, our view (src/main/res/layout/text_fragment_layout.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/my_text_view"/>
</LinearLayout>


Next, the Fragment code (src/main/java/com/jameskbride/TextFragment.java):
package com.jameskbride;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class TextFragment extends Fragment{
    public static TextFragment newInstance() {
        return new TextFragment();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.text_fragment_layout, container, false);

        return root;
    }
}


Let's break this down. First, in our newInstance() method we have returned a TextFragment. Second, in the onCreateView method we inflate our view, text_fragment_layout.xml, which contains the TextView with our "Hello world!" string. The test should now pass.  This is just a basic example of how to unit test fragments in Android.

One thing to keep in mind is that just as activities can be tested in their various life-cycle events, so too can fragments.  If we need to to test code in onAttach() method we can simply call it directly.   The technique for testing Fragments is essentially the same as testing activities. 

Hopefully this has been a useful starting point, though one which has been a long while coming!



Saturday, September 12, 2015

Broken Windows and How to Fix Them

It's your first day on the job at a new client and you're excited and ready to get started.  They've got some cool problems they are trying to solve, the tech stack sounds interesting, and you've got work lined up for your team.  You get there and immediately notice that the build board is red.  Not just a little red, but bleeding red.  "Maybe it's just a single bad build" you think to yourself. "I'm sure it will get fixed right away."  

You try not to about it too much while you get settled in and start pulling down the code to build it locally.  It compiles, but there are a number of failing tests.  You notice a number of other details about the code; domain logic is spread throughout, there is bleed-over between the different layers of abstraction, and A LOT of untested code.  Code duplication abounds, and some of the tests are flaky.
You check the build board again, and notice that not only is it still red, but someone has pushed on top with more changes.  When you ask one of the other developers about it they chuckle and reply that it has been red for a while, but it's OK, it's just some of the tests.  No one seems too bothered by this.  You start wondering if you've walked into some parallel universe where a red build is acceptable, or even expected.  How did it get this way? Doesn't this bother anyone? You realize you have your work cut out for you.

Broken Window Theory

So what happened?  How did the state of the project get to this point?  How can a build get into a red state and stay that way?  Unfortunately it is entirely too easy for this to happen.  It only takes one bad changeset and zero developers who care about fixing it.  This happens in software development with maddening frequency, and is an example of the Broken Window Theory.

Broken Window Theory goes like this:

Given a building with one or more broken windows which are not quickly repaired, the tendency is for more windows to be broken and for other acts of vandalism to occur.  People notice that no one cares about the building, and there is no social pressure to prevent the vandalism.  The building quickly falls into disrepair and stays that way.

In software development lots of little changes can contribute to creating an environment which tolerates a continually broken build.  The addition of code which is difficult to test leads to fewer tests. Flaky tests can degrade confidence in the build ("Oh yeah, that build is red because of known flaky test, go ahead and push anyway!"). Just plain broken functionality is allowed to be pushed, and a general lack of design and maintenance can cause the codebase to be become chaotic and difficult to work with.  Maybe there are developers on the team who simply don't know how to write tests, or how to refactor properly.

All of this together can make it disheartening to try to do the right thing, it becomes easy to fall into the trap of "Well, this is just how the codebase is, we should just learn how to live with it." Obviously this is not the answer.

Fixing Broken Windows

So the windows have been broken and the build is red.  How do you fix the situation?  How do you get the build back to a green state and keep it that way?  The answer is simple, if not easy: get the team to care about the build.  Obviously this is easier said than done in many cases.  Often the team has been beaten down by the long-standing degradation of the build, so simply shouting about the problems in the codebase won't solve anything.  Concrete steps must be taken.

Stop Further Damage

To begin with, cordon off the building, draw a line in the sand, stop the bleeding (pick your metaphor here, there are a lot of them), but whatever you do stop the code from getting any worse than it already is.

Take your lead from this guy.

Be prepared to put the breaks on and play the bad guy, because you're about the rock the boat and upset a lot of people who have become comfortable with The Way Things Are.

Your first order of business should be to get the build back to green as quickly as possible.  If that means no one pushes code until that happens then so be it.  Implement an Evergreen Policy which states that if a build goes red it is either fixed immediately, or the changeset is backed out.

The build is an indication of the health of the project, and ultimately it should tell you whether you are ready to deliver or not, so it should become a priority to get it green and keep it green.  The code should compile and all tests should pass every single time.

Make "Done" Include a Healthy Build

For a given unit of work (user story, task, whatever you want to call it) there should be a Definition of Done.  This definition should describe the requirements to be met before that unit of work can be considered complete, and the next unit of work is begun.  Whatever those requirements currently are, they should be updated to include a clean bill of health for the build.  Appropriate tests should be added and the entire build should complete successfully.  Nobody gets to pick up another piece of work until this happens.  Refuse to allow breaking changes into the build.

Increase Confidence

By now you've hopefully gotten the build green, and laid out a plan to keep it green.  Even so, there may be tests which you can't always trust.  These tests might flap occasionally, leaving developers unsure if they've broken something or not.  If you can't trust the build you can't be sure of the quality of the software you're building.  Investigate the root cause of the flapping tests and address it as quickly as possible.  In the meantime take steps to fix the tests themselves as well.  Maybe they need to be rewritten, or moved into another, more stable layer.  Maybe there are timing issues which can solved by increasing the timeout values.  Maybe they should just be eliminated, as flapping tests are useless in terms of confidence in the build. Whatever the case, do whatever you have to create a reliable and consistent build.

Spread the Pain

Don't try to take on the world on your own. Instead, enlist everyone else to your cause.


Make everyone responsible for keeping the build green.  Chances are good you're going to need backing from the technical leadership to get everyone in line, as behavior doesn't change overnight and some developers will need an incentive to change.  Maybe this means a rotating responsibility in the beginning, but the goal is to make everyone responsible for the entire codebase, and at the very least responsible for the code they are pushing.

Educate and Train

A major cause of headache-inducing codebases is that many developers simply don't know how to do better than they already are.  They may not know how to properly unit test, or maybe they haven't been introduced to Test-Driven Development before.  Maybe they are simply inexperienced and need to be educated on common engineering practices and principles, such as SOLID, DRY, and the concepts of clean code.  Take this opportunity to help everyone step up their game.  Start holding a regular code club to practice these principles away from the production code.  Start a programming book club and encourage everyone to participate.  Do something to help everyone improve, as this will pay out for everyone, both in the short term and in the long term.

Communicate

It's amazing how often problems can be solved by simply talking about them.  Encourage the team to communicate about the issues they're having, especially when it concerns a broken build.  Often, simply acknowledging that the build is broken is enough to start a conversation about how to fix it.  Also encourage communication even when the build is not broken; make it a point to regularly get together and talk about what could be improved, both within the code and without.  It doesn't have to be an hour long meeting, it could just as easily be 15 minutes or less, which is enough time to get the team thinking about the problem at hand.

Give It Time

Change doesn't happen immediately, but if you keep at it and keep everyone on their toes it will happen sooner or later.  Eventually you'll notice a difference; instead of the build being red and people just shrugging it off you'll start overhearing conversations about how to get it fixed and fast.  Just as people got used to the build being broken all the time they'll become used to the build being green all the time.  They won't tolerate broken windows, and they'll make an effort to fix them as soon as they happen.



Saturday, September 5, 2015

Android Dev and API Keys - Keep It Secret, Keep It Safe

The Problem

You have an external API you must call which requires an API key.  This key must not be checked into source control.  Maybe you have other values which are different depending what environment you're in. Either way, you don't want these hard-coded into your source code.

The Solution

Disclaimer: The following solution was synthesized from a couple of disparate StackOverflow answers which I am now unable to find.

1. Create a properties file in which to store your sensitive security/environment values.  Properties in this file follow the standard format of key=value.  Lines may be commented out by prefixing them with the "#" symbol.  You'll want to create a properties file for each environment you'll be working in (e.g. debug, release, etc).

IMPORTANT: Add each of these files to your .gitignore file (or your version control equivalent).

Example properties file:
key.password=somepassword
#key.alias=somekeyalias This line is commented out
store.file=/home/jim/keystores/mykeystore.store
api.key=myapikey
store.password=mystorepassword

2. Update your build.gradle file pull the values out of your property file for each environment.

In app/build.gradle:
android {
    //Lots of other configuration stuff not shown here.
    buildTypes {
        debug {
            Properties properties = new Properties()
            properties.load(project.rootProject.file('local.properties').newDataInputStream())
            def apiKey = properties.getProperty('api.key')
            resValue "string", "api_key", apiKey
            // other debug config stuff not shown here            
        }
        release {
            Properties properties = new Properties()
            properties.load(project.rootProject.file('release.properties').newDataInputStream())
            def apiKey = properties.getProperty('api.key')
            resValue "string", "api_key", apiKey            
            // other release config stuff not shown here
        }
    }
}

Notice the lines where we are calling 'resValue "string, "api_key", apiKey'? This is essentially telling the build to replace the resource value "api_key" anywhere it is used with the value in the variable apiKey.

3. Add a string value to your res/values/strings.xml file to refer to your value.
In res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>    
    <string name="api_key_string">@string/api_key</string>
    <!-- Other values not shown here -->
</resources>

4. Update your AndroidManifest.xml file to make your values available to the application via meta-data.

In app/src/main/AndroidManifest.xml:
<?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="com.yourpackage">
    <!-- Other configuration not shown here -->
    <application
        android:name=".some.applicationName"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
            <!-- Other configuration not shown here -->
        <meta-data android:name="api-key"
            tools:replace="android:value"
            android:value="@string/api_key_string"></meta-data>
            <!-- Other configuration not shown here -->
    </application>
</manifest>

Notice that on our <meta-data> tag we have defined an android:name attribute with the value "api-key", and we are using tools:replace="android:value".  This allows us to point to the value being held in our strings.xml file, referred to as "@string/api_key_string".

5. Finally, we can now refer to our value in the production code by accessing it via a Bundle.

Production Code:

        //You'll need to get a hold of your ApplicationContext for this step.  
        String apiKey;
        try {
            ApplicationInfo applicationInfo = yourApplicationContext.getPackageManager()
                    .getApplicationInfo(yourApplicationContext.getPackageName(),
                            PackageManager.GET_META_DATA);
            Bundle bundle = applicationInfo.metaData;
            apiKey = bundle.getString("api-key");
            Log.d(TAG, "api key: " + apiKey);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();  //Do something more useful here!
        }

That's it! By having your production code access this value via Bundle you've effectively abstracted away any environment-specific concerns; neither do you need to hard-code values anywhere in your source tree. This allows you to safely develop code which is portable to any environment, and frees you from worrying about someone getting access to your security-sensitive values simply by checking out your code.