2013年7月17日水曜日

テスト用DBを使ってActivityInstrumentationTestCase2のテストを行う

RenamingDelegatingContextをSQLiteOpenHelperのコンストラクタに渡すと、テスト用のDBを使用することができます。
RenamingDelegatingContextはこちらの記事が詳しいです。

u1aryzの備忘録とか: RenamingDelegatingContextを使ってみた

前回の記事[Android] CursorLoaderのテストではMockコンテキストとMockContentResolverを使用して、CursorLoaderにRenamingDelegatingContextを使って初期化したContentProviderを渡してテスト用のDBにアクセスさせました。
また、Mockコンテキストを使用するためにActivityUnitTestCaseを用いました。

ActivityUnitTestCaseはMockコンテキストを利用できるので、対象のアプリケーションを変更する必要がない点が利点ですが、Robotium等を使ったテストなどはActivityInstrumentationTestCase2を使う必要があります。ActivityInstrumentationTestCase2ではMockコンテキストを利用できないため別の方法でRenamingDelegatingContextを対象のアプリケーションに渡さなければいけません。

テスト対象のコードをいじる以外にあまりいい方法を思いつかなかったのですが、ContentProvider内部でlazy valを使ってSQLiteOpenHelperの初期化を遅らせ、初めてDBにアクセスする必要がある段階でisTestフラグを見てテスト中ならRenamingDelegatingContext、本番ならgetContextのコンテキストをそれぞれ使用してSQLiteOpenHelperを初期化するというアプローチをとりました。

object UnitsProvider {
  val AUTHORITY = "com.shinichy.convertBox.provider"
  private val TEST_PREFIX = "test_"
  var isTest = false
}

class UnitsProvider extends ContentProvider {
  ...

  private lazy val unitsDBHelper: UnitsDBHelper = {
    Log.d(getClass.getSimpleName, "unitsDBHelper initialized")
    val context = if (UnitsProvider.isTest) {
      Log.d(getClass.getSimpleName, "UnitsProvider is in test mode")
      new RenamingDelegatingContext(getContext, UnitsProvider.TEST_PREFIX)
    } else {
      getContext
    }
    new UnitsDBHelper(context)
  }

  // アプリケーション起動時にこのonCreateが呼ばれるため、ここではSQLiteOpenHelperの初期化は行わない
  def onCreate(): Boolean = {
    true
  }

  ...

  class UnitsDBHelper(context: Context) extends SQLiteOpenHelper(context, UnitsDBHelper.DB_NAME, null, UnitsDBHelper.DB_VERSION) {
   ...

lazy valはJavaにはないため、もしJavaで同じ事を実現するなら以下の様なメソッドを用いて同じ事が実現できると思います。もちろんhelperは直接使わず、SQLiteOpenHelperが必要な箇所は全てgetHelper()を使用するようにします。

private SQLiteOpenHelper helper = null;

private SQLiteOpenHelper getHelper() {
    if (helper == null) {
        Context context = null;
        if (isTest) {
            context = new RenamingDelegatingContext(getContext(), TEST_PREFIX);
        } else {
            context = getContext();
        }
        helper = new UnitsDBHelper(context);
    }
    return helper;
}

以下がテストコードの抜粋です。

class MainActivityTest extends ActivityInstrumentationTestCase2(classOf[MainActivity]) {

  var solo: Solo = null
  var activity: MainActivity = null

  override def setUp() {
    UnitsProvider.isTest = true
    solo = new Solo(getInstrumentation, getActivity)
    activity = getActivity
  }
  ...

setUpの最初にisTestフラグを立てます。これ以降最初にunitsDBHelperを使用する箇所でlazy valの箇所が評価され、RenamingDelegatingContextを用いてUnitsDBHelperが初期化され、テスト用のDBが使用されるようになります。

RenamingDelegatingContextはandroid.testパッケージにあるため、AndroidManifest.xmlのapplication要素の中に以下のuses-libraryと書いておかないとエラーログがはき出されてしまいます。

<uses-library android:name="android.test.runner"/>

0 件のコメント:

コメントを投稿