Library ProjectとInstrumentation Test

ADT 0.9.7から追加された「Library Project」により、複数のアプリケーションから参照できるライブラリィプロジェクトを構成できるようになったのは前回書いた。

ADTによるLibrary Projects

Library Perojectにより、アプリケーションはライブラリィコードを共有できるようになったので、とても便利になったのだが、副作用として困った問題が発生する。それはAndroid SDK独特のInstrumentationを利用したテスト時である。

Library Project以前は、ライブラリィプロジェクトであってもAndroidプロジェクトとして管理する限りは、Android Package(.apk)を生成することが出来た。Instrumentationによるテストはテスト用のパッケージと分離されていることを前提にしている、他のパッケージをテストすることが目的のため、ライブラリィプロジェクトをテストするには、テスト対象のパッケージを明示する必要があった。

悩めるテスト

ライブラリィをテストする、テストプロジェクトのマニフェスト

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
   package="org.droid.test" android:versionCode="1" android:versionName="1.0">
   <activity android:name="org.droid.test.TestActivity"/>
  <instrumentation 
     android:targetPackage="org.droid" 
     android:name="android.test.InstrumentationTestRunner" />
</manifest> 

従来はinstrumentation要素のandroid:targetPackageに、明示的にパッケージを指定することで、ライブラリィコードをテストが出来たのだが、Library Projectは「Is Library」をチェックすることによりAndroid Package(.apk)を生成しないため、上記マニフェストでは「パッケージが見つからない」とエラーになってしまうのである。

さて、解決策はあるのか?

SDKのリファレンス文書には、以下のように書いてある。
Developing In Eclipse, with ADT#Working with Library Projects

Testing a library project

There are two recommended ways of setting up testing on code and resources in a library project:

•You can set up a test project that instruments an application project that depends on the library project. You can then add tests to the project for library-specific features.

•You can set up a set up a standard application project that depends on the library and put the instrumentation in that project. This lets you create a self-contained project that contains both the tests/instrumentations and the code to test.

一つはアプリケーションをテストするテストプロジェクトを構成する際に、アプリケーションから参照されているライブラリィコードもテストする方法、もう一つはプロジェクトにテストコードとインストゥルメンテーションの両方を含むよう、通常のアプリケーションプロジェクトとしてセットアップする方法とのこと。

アプリケーションをテストする際にライブラリィもテストする、ということは通常は考えにくいので、後者を選ぶことにするが、その場合、Library Projectのパッケージ(.apk)は生成されないので、テストのパッケージとインストゥルメンテーションのパッケージは同一となる訳だ。

  .test" 
     android:name="android.test.InstrumentationTestRunner"/>

なお、このようにテスト対象のパッケージが変わる(例だと org.droid.lib -> org.droid.test)場合、ActivityInstrumentationTestCase2クラスなどを使っているならば、コンストラクタのパラメタでパッケージ名を渡しているので注意が必要だ。

public class HogeTest extends ActivityInstrumentationTestCase2 {
    public HogeTest() {
        super("org.droid", TestHogeActivity.class);
    }

上記コンストラクタで渡すパラメタは、テスト時にgetActivityメソッドが呼ばれた際に、生成されるインテントからアクティビティを特定するためのパッケージ名として使われるため、属しているアクティビティが解決できないとエラーになってしまうのだ。(intent does not resolve) インテントの未解決エラーはランタイムエラーであり、コンパイル時には解らないので注意が必要だ。