All Android applications are comprised of four major components: Activities, Content Providers, Services and Broadcast Receivers. What little testing and test automation that happens in this community is focused primarily on the Activity layer — if at all. A stroll down to your local tech bookstore all but confirms this fact.

But what if you wanted to have a suite of automated checks for something other than Activities? Or, shock!, you want to do something akin to TDD? Well then you would be into the great unknown. And is where I found myself even though I dislike Java, loathe Eclipse and am just learning Android. In this case things worked in my favour since I keep falling off the TDD wagon as I ‘know’ how to do something in Python or Ruby but with Java I’m using it as a learning crutch (stick?).

First, we have the test. Or at least what it ended up at as I understood the problem better. Kinda a Red/Refactor, Red/Refactor cycle.

public void testDatabaseCreated() {
    Cursor cursor;
    Uri url = Credentials.CredentialsColumns.CONTENT_URI;
    cursor = mResolver.query(url, null, null, null, null); 
    assertEquals(3, cursor.getColumnCount());
    cursor.close();
}

Essentially all this does is make sure that the [test] database was created and that it has 3 columns — they could be the wrong three, but at least there is three.

Of course along the way I had to make that file compile by fixing the constructor and understanding the not-really-documented-because-no-one-test-android-apps MockContentResolver. This class will make a backup of your application’s database and restore it when the run is done. Not quite Mocking, but close enough for what is necessary here.

Now to make it Green. Which wasn’t as straight forward as I had hoped. Showing my ignorance, I had thought there is ‘supposed’ to a 1:1 mapping of test classes to code classes. Not in the Android world there isn’t. In this case it is a 1:3 mapping which makes me think I’m not quite doing it ‘by the book’ but there is no book on doing TDD for Android. (Well, there is Android Application Testing Guide which is ok, but there is a definite market need here still.)

The first class I needed to make was the actual Content Provider class, which in typical Java style was a ‘right click and let the IDE to magic for you’. You end up with a class with six overridden methods. But naturally they don’t do anything.

Oh. And have I mentioned I decided to throw the additional twist of having my Content Provider interface with the build-in SQLite database?

Android is I guess what you would call an Event Driven environment where you override specific events to get your desired behaviours. So in my Content Provider I needed to override the onCreate method first.

@Override
public boolean onCreate() {
    dbHelper = new DatabaseHelper(getContext());
    return true;
}

Which creates our DAO for this particular Content Provider. DatabaseHelper is actually an inner class of the Content Provider.

public class DatabaseHelper extends SQLiteOpenHelper {
    public DatabaseHelper(Context context) {
       super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
  
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE " + CREDENTIALS_TABLE_NAME + " (" + CredentialsColumns.CREDENTIAL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
                                 + CredentialsColumns.USERNAME + " VARCHAR(255),"
                                 + CredentialsColumns.PASSWORD + " VARCHAR(255)" + ");");
    }

    @Override
    public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {

    }

}

As you can see, I haven’t worried about doing the onUpgrade scenario yet. (And that the new class wizard names arguments horribly.)

I had tried originally to create the DatabaseHelper class as a separate class so it cold be tested in isolation, but there is no information anywhere that I could find that describes how to do it. So I put it as an inner class as the examples I could understand all had it there.

The third class that I needed to create just so my simple test could pass is the one that represented the actual table. It is pretty boring, except for the CONTENT_URI line which is important.

public final class Credentials {
    private Credentials() {}

    public static final class CredentialsColumns implements BaseColumns {
        private CredentialsColumns() {}
    
        public static final Uri CONTENT_URI = Uri.parse("content://" + SauceLabsCredentialsProvider.AUTHORITY + "/credentials");

        public static final String CREDENTIAL_ID = "_id";
        public static final String USERNAME = "username";
        public static final String PASSWORD = "password";
    }
}

Access to/from a Content Provider is done through a URI. (If you are familiar with RoR RESTful URL’s you’ve got the concept.) In this case the CONTENT_URI is specific to a credentials table that the Content Provider has the ‘Authority’ to access. To be compliant with the spirit of Android development, you don’t do raw SQL against the database, but instead do queries against a URI.

This is enough code to create a database on the emulator/device and to allow my test to create the mocked one.

Or would have been if I had remembered to register it in the manifest and then didn’t totally screw up my environment with a package rename gone hopelessly wrong. (If you get a deployment claiming the Content Provider is already installed, check that your paths in terms of code match your manifest.)

Once I solved that I still had a null pointer on the query itself — because I hadn’t implemented it yet. (It was late and my brain was shutting off…)

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
    qb.setTables(CREDENTIALS_TABLE_NAME);

    SQLiteDatabase db = dbHelper.getReadableDatabase();

    Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
    return c;
}

And then I was green.

Now what I have facing me is

public void testInsertCredentials() {

}

public void testQueryCredentials() {

}

public void testDeleteCredentials() {

}

which will force me to flesh out more of the overridden methods. Only when I have those all green will I actually wire it up to an Activity. So it is possible to do something like TDD for Android Content Providers — its just not straight forward nor documented well. Or at all.