Wednesday, February 8, 2012

Android testing: configure preferences and database

I started experimenting with unit and functional tests for my Android app. It took me some time to find out how to set up the tests with special preferences and database files.
I tried to use the mock contexts that the Android testing framework provides, but ran into the following problems:
  • The support for testing content providers was not applicable, because I am still using the older, simpler design of the database helper class bassed on SQLiteOpenHelper
  • A simple RenamingDelegatingContextdid work with ActivityUnitTestCase, but testing dialogs did not work there.
  • Apparently the framework does not provide any support for renaming the default preferences file
Finally, I found a useful suggestion at Android Functional Testing vs Dependency Injection 
Using that pattern I found a way to configure my database adapter with different database file names for the live app and the test drive.

My application class reads:

public class MyApplication extends Application {
    private SharedPreferences settings;
    private String databaseName;

    @Override
    public void onCreate()
    {
        super.onCreate();
        if (settings == null)
        {
            settings = PreferenceManager.getDefaultSharedPreferences(this);
        }
        if (databaseName == null) {
          databaseName = "data";
        }
    }

    public SharedPreferences getSettings()
    {
        return settings;
    }

    public void setSettings(SharedPreferences s)
    {
        settings = s;
    }
    public String getDatabaseName() {
      return databaseName;
    }
    public void setDatabaseName(String s) {
      databaseName = s;
    }
}
And the relevant parts of the database adapter class:

public class ExpensesDbAdapter {
  private String mDatabaseName;

  public ExpensesDbAdapter(Context ctx) {
    this.mCtx = ctx;
    mDatabaseName = ((MyApplication) ctx.getApplicationContext()).getDatabaseName();
  }
  public ExpensesDbAdapter open() throws SQLException {
    mDbHelper = new DatabaseHelper(mCtx,mDatabaseName);
    mDb = mDbHelper.getWritableDatabase();
    return this;
  }
  private static class DatabaseHelper extends SQLiteOpenHelper {

    DatabaseHelper(Context context,String databaseName) {
      super(context, databaseName, null, DATABASE_VERSION);
    }
  }
And setting a different name in the test case works with

  protected void setUp() throws Exception {
    super.setUp();
    MyApplication app = (MyApplication) getInstrumentation().getTargetContext().getApplicationContext();
    app.setSettings(app.getSharedPreferences("functest",Context.MODE_PRIVATE));
    app.setDatabaseName("functest");
}
You can find the complete example in the source code of MyExpenses.

1 comment: