アプリの例をターゲットにする

このカテゴリのインストルメンテーション テストは、通常の Android アプリをターゲットとするテストとそれほど変わりません。留意点としては、インストルメンテーションを含むテスト アプリケーションは、ターゲットとするアプリケーションと同じ証明書で署名する必要があります。

このガイドは、プラットフォーム ソースツリーのワークフローに関する知識があることを前提としています。知識をお持ちでない場合は、要件を参照してください。ここでは、独自のテスト アプリケーション パッケージをターゲット パッケージに設定して新しいインストルメンテーション テストを作成する方法を説明します。コンセプトに精通されていない場合は、プラットフォーム テストの概要をご覧ください。

このガイドでは、次のテストを例として使用します。

  • frameworks/base/packages/Shell/tests

先に進む前に、まずコードを確認して、おおよそのイメージをつかむことをおすすめします。

ソースの場所を決定する

インストルメンテーション テストはアプリケーションを対象としているため、テスト ソースコードはプラットフォーム ソースツリーのコンポーネント ソース ディレクトリのルート下にある tests ディレクトリに配置するのが慣例となっています。

自己インストルメンテーション テストのエンドツーエンドの例のソースの場所に関する説明をご覧ください。

マニフェスト ファイル

通常のアプリケーションと同様に、各インストルメンテーション テスト モジュールにはマニフェスト ファイルが必要です。ファイル名を AndroidManifest.xml にしてテスト モジュール用の Android.mk と同じ場所に置くと、BUILD_PACKAGE コアの makefile によって自動的に取り入れられます。

先に進む前に、まずアプリ マニフェストの概要の確認をおすすめします。

マニフェスト ファイルの基本コンポーネントとその機能の概要がわかります。

サンプルの gerrit 変更のマニフェスト ファイルの最新バージョンは、次の URL からアクセスできます。 https://android.googlesource.com/platform/frameworks/base/+/main/packages/Shell/tests/AndroidManifest.xml

説明のため、スナップショットを以下に示します。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.shell.tests">

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

        <activity
            android:name="com.android.shell.ActionSendMultipleConsumerActivity"
            android:label="ActionSendMultipleConsumer"
            android:theme="@android:style/Theme.NoDisplay"
            android:noHistory="true"
            android:excludeFromRecents="true">
            <intent-filter>
                <action android:name="android.intent.action.SEND_MULTIPLE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="*/*" />
            </intent-filter>
        </activity>
    </application>

    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
        android:targetPackage="com.android.shell"
        android:label="Tests for Shell" />

</manifest>

マニフェスト ファイルで、いくつか注目する点があります。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.shell.tests">

package 属性はアプリケーション パッケージ名です。これは、Android アプリケーション フレームワークがアプリケーション(ここではテスト アプリケーション)を識別するために使用する一意の識別子です。システム内の各ユーザーは、このパッケージ名のアプリケーションを 1 つのみインストールできます。

これは、テスト対象のアプリケーション パッケージから独立したテスト アプリケーション パッケージであるため、別のパッケージ名を使用する必要があります。接尾辞 .test を追加するのが一般的です。

さらに、この package 属性は ComponentName#getPackageName() が返すものと同一であり、adb shell 内のさまざまな pm サブコマンドで使用するパッケージとも同一です。

また、パッケージ名は通常 Java パッケージ名と同じスタイルですが、実際にはほとんど関係ありませんので、ご注意ください。つまり、アプリケーション(またはテスト)のパッケージには任意のパッケージ名のクラスを含めることができますが、単純にアプリケーションやテストの最上位 Java パッケージ名をアプリケーション パッケージ名と同一にすることもできます。

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

これは、すべてのインストルメンテーション テストに必要です。なぜなら、関連するクラスが別のフレームワーク JAR ライブラリ ファイルにパッケージ化されていることにより、テスト パッケージがアプリケーション フレームワークで呼び出される際に追加のクラスパス エントリが必要になるためです。

android:targetPackage="com.android.shell"

上記により、インストルメンテーションのターゲット パッケージが com.android.shell に設定されます。インストルメンテーションが am instrument コマンドで呼び出されると、フレームワークは com.android.shell プロセスを再起動し、インストルメンテーション コードをテスト実行プロセスに挿入します。つまり、テストコードは、テスト対象のアプリケーションで実行されているすべてのクラス インスタンスにアクセスでき、公開されているテストフックに応じて状態を操作できる可能性があります。

シンプルな構成ファイル

新しいテスト モジュールごとに、モジュール メタデータ、コンパイル時の依存関係、パッケージ化手順をビルドシステムに指示するための構成ファイルが必要です。ほとんどの場合、Soong ベースのブループリント ファイル オプションで十分です。詳しくは、シンプルなテスト構成をご覧ください。

複雑な構成ファイル

より複雑なテストの場合、Android のテストハーネスである Trade Federation のテスト構成ファイルも作成する必要があります。

テスト構成では、特別なデバイス セットアップ オプションとデフォルト引数を指定して、テストクラスを提供できます。

サンプルにおける gerrit 変更の構成ファイルの最新バージョンは、frameworks/base/packages/Shell/tests/AndroidTest.xml からアクセスできます。

説明のため、スナップショットを以下に示します。

<configuration description="Runs Tests for Shell.">
    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
        <option name="test-file-name" value="ShellTests.apk" />
    </target_preparer>

    <option name="test-suite-tag" value="apct" />
    <option name="test-tag" value="ShellTests" />
    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
        <option name="package" value="com.android.shell.tests" />
        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
    </test>
</configuration>

テスト構成ファイルで、いくつか注目する点があります。

<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
  <option name="test-file-name" value="ShellTests.apk"/>
</target_preparer>

これは、指定した target_preparer を使用して、ShellTests.apk を対象デバイスにインストールするように Trade Federation に指示します。Trade Federation のデベロッパーが利用できるターゲット作成ツールは数多くあります。これを使用して、テスト実行前にデバイスを適切にセットアップできます。

<test class="com.android.tradefed.testtype.AndroidJUnitTest">
  <option name="package" value="com.android.shell.tests"/>
  <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
</test>

これは、テストの実行に使用する Trade Federation テストクラスを指定し、実行するデバイス上のパッケージとテストランナー フレームワーク(この場合は JUnit)を渡します。

詳しくは、テスト モジュール構成をご覧ください。

JUnit4 の機能

android-support-test ライブラリをテストランナーとして使用すると、新しい JUnit4 スタイルのテストクラスの導入が可能になります。サンプルの gerrit 変更には、この機能のごく基本的な使用例が含まれています。

サンプルにおける gerrit 変更の最新のソースコードは frameworks/base/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java からアクセスできます。

通常、テストパターンはコンポーネント チームに固有のものですが、一般的に有用な使用パターンがあります。

@SmallTest
@RunWith(AndroidJUnit4.class)
public final class FeatureFactoryImplTest {

JUnit4 の主な違いは、テストを共通の基本テストクラスから継承する必要がなくなったことです。代わりにプレーンな Java クラスでテストを作成し、アノテーションを使用してテストのセットアップと制約を指定します。この例では、このクラスを Android JUnit4 テストとして実行するように指示しています。

@SmallTest アノテーションは、テストクラス全体のテストサイズを指定しています。このテストクラスに追加されたすべてのテストメソッドは、テストサイズのアノテーションを継承します。テスト前のクラスのセットアップ、テスト後のティアダウン、テスト後のクラスのティアダウン: JUnit4 の setUp および tearDown メソッドに似ています。 Test アノテーションは、実際のテストにアノテーションを付加するために使用されます。

    @Before
    public void setup() {
    ...
    @Test
    public void testGetProvider_shouldCacheProvider() {
    ...

@Before アノテーションは、JUnit4 によってメソッドで使用され、テスト前のセットアップを行います。この例には使用されていませんが、テスト後のティアダウンに使用される @After もあります。 同様に、@BeforeClass@AfterClass のアノテーションは JUnit4 によりメソッドで使用され、テストクラス内のすべてのテストを実行する前にセットアップを行い、後でティアダウンできます。クラススコープのセットアップ メソッドとティアダウン メソッドは静的である必要があります。

テストメソッドについては、以前のバージョンの JUnit とは異なり、メソッド名を test で開始する必要はありませんが、代わりに各メソッドに @Test アノテーションを付ける必要があります。通常、テストメソッドはパブリックであり、戻り値を宣言せず、パラメータを受け取りません。また、例外をスローする場合があります。

        Context context = InstrumentationRegistry.getTargetContext();

JUnit4 テストでは、共通の基本クラスが不要になったため、getContext() または getTargetContext() を使用して基本クラスメソッドで Context インスタンスを取得する必要がなくなりました。代わりに、新しいテストランナーは InstrumentationRegistry でそれらを管理します。ここには、インストルメンテーション フレームワークで作成されたコンテキストと環境のセットアップが保存されます。このクラスを使用して、以下を呼び出すこともできます。

  • getInstrumentation(): Instrumentation クラスに対するインスタンス
  • getArguments(): -e <key> <value> によって am instrument に渡されるコマンドライン引数

ローカルでのビルドとテスト

最も一般的なユースケースでは、Atest を使用します。

より多くのカスタマイズが必要な複雑なケースでは、インストルメンテーションの手順を実施してください。