android-test-kitで使われるInstrumentationRegistryの理解を深める

android-test-kitの理解を深めようと、以下を眺めていました。

https://code.google.com/p/android-test-kit/wiki/AndroidJUnitRunnerUserGuide

この中で、JUnit4の枠組みの中でContextを取得するためのInstrumentationRegistryがどのように振る舞うのか気になったので少し追ってみました。

InstrumentationRegistry is an exposed registry instance that holds a reference to the instrumentation running in the process and it’s arguments and allows injection of the following instances:

Androidのテストフレームワークは多くの方は既知だと思いますが、

ref: http://developer.android.com/tools/testing/testing_android.html

のようになっていて、テスト対象のアプリケーションパッケージと、テストパッケージ、その間にInstrumentationTestRunnerが存在します。

android-test-kitを導入する前までは、このアプリケーションパッケージ/テストパッケージのコンテキストを取得するためには、テストケースのクラスがAndroidTestCaseもしくはInstrumentationTestCaseを継承する必要がありました。

一方で、android-test-kit導入すると、それらの継承なしでコンテキストを手に入れる手段があります。それがInstrumentationRegistryです。

いったん、このandroid-tets-kitが提供するAPI群を見てみます。すると、android.support.test配下にInstrumentationRegistryが提供されているのがわかります。

そのリファレンスを進むと、Public Methodとして以下が提供されていることがわかります。

  • getArguments()
  • getContext()
  • getInstrumentation()
  • getTargetContext()
  • registerInstance(Instrumentation instrumentation, Bundle arguments)

おやおや。registerInstanceだけが先ほどのwikiで説明されていませんでした。となると、名前からしてもここが怪しそうです。registerInstanceの説明にも

Records/exposes the instrumentation currently running and stores a copy of the instrumentation arguments Bundle in the registry.
This is a global registry – so be aware of the impact of calling this method!

とかかれていました。

ちょっと、コードを見てみます。すると、以下のようなコードがみつかります。

  • support/src/android/support/test/internal/runner/InstrumentationRegistry.java
public static void registerInstance(Instrumentation instrumentation) {
    sInstrumentationRef.set(instrumentation);
}

なるほど。これをつかえばGlobal Scopeで、このメソッドに対してInstrumentationを登録できるのですね。では、これはどこで呼ばれているのだろうか…と探すと、以下に見つけました。

/**
* Sets up lifecycle monitoring, and argument registry.
* <p>
* Subclasses must call up to onCreate(). This onCreate method does not call start()
* it is the subclasses responsibility to call start if it desires.
* </p>
*/
@Override
public void onCreate(Bundle arguments) {
  Log.i(LOG_TAG, "Instrumentation Started!");
  logUncaughtExceptions();
  InstrumentationRegistry.registerInstance(this);
  ActivityLifecycleMonitorRegistry.registerInstance(mLifecycleMonitor);
  InstrumentationArgumentsRegistry.registerInstance(arguments);
  mHandlerForMainLooper = new Handler(Looper.getMainLooper());
  mMainThread = Thread.currentThread();
  mExecutorService = Executors.newCachedThreadPool();
  Looper.myQueue().addIdleHandler(mIdleHandler);
  super.onCreate(arguments);
}

なるほど。では、これはどこで呼ばれているのか。ふと思い立ってみると、このandroid-test-kitを使うために以下のようにGradle.configに設定する必要がありました。ここが怪しそう。

android {
    // .....
    defaultConfig {
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
}

android.support.test.runner.AndroidJUnitRunner のコードを見てみると、見事、 MonitoringInstrumentation を継承していました。

public class AndroidJUnitRunner extends MonitoringInstrumentation {

これでスッキリしました。なるほどなるほど。

  1. InstrumentationRegistry.registerInstance(this)がテスト実行時に呼ばれ、コンテキストが登録される
  2. 設定されたコンテキストはInstrumentationRegistry.getContext()なんかでどこからでも取得できるようになる

InstrumentationRegistry.registerInstanceはpublicだけれど、影響が大きく、Wikiにも公開されていないので変に使うとテストが意図しなくなるとかありそう。なので理解に止めましょう。

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s