Beispiel für die Ausrichtung auf eine App

Diese Kategorie von Instrumentierungstests unterscheidet sich nicht wesentlich von denen, die auf reguläre Android-Anwendungen ausgerichtet sind. Die Testanwendung, die die Instrumentierung enthält, muss mit demselben Zertifikat signiert sein wie die Anwendung, auf die sie ausgerichtet ist.

In diesem Leitfaden wird davon ausgegangen, dass Sie bereits einige Kenntnisse zum Workflow des Plattform-Quellbaums haben. Falls nicht, lesen Sie den Abschnitt Anforderungen. Im hier beschriebenen Beispiel wird ein neuer Instrumentierungstest mit einem eigenen Testanwendungspaket als Zielpaket geschrieben. Wenn Sie mit dem Konzept nicht vertraut sind, lesen Sie den Hilfeartikel Einführung in Plattformtests.

In diesem Leitfaden wird der folgende Test als Beispiel verwendet:

  • frameworks/base/packages/Shell/tests

Wir empfehlen, sich zuerst einen Überblick über den Code zu verschaffen, bevor Sie fortfahren.

Quellspeicherort festlegen

Da der Instrumentierungstest auf eine Anwendung ausgerichtet ist, wird der Test-Quellcode gemäß der Konvention im Stammverzeichnis des Komponentenquellenverzeichnisses im Plattform-Quellbaum in einem tests-Verzeichnis abgelegt.

Weitere Informationen zum Speicherort der Quelle finden Sie im End-to-End-Beispiel für selbstinstrumentierte Tests.

Manifestdatei

Genau wie bei einer regulären Anwendung benötigt jedes Instrumentierungstestmodul eine Manifestdatei. Wenn Sie die Datei AndroidManifest.xml nennen und sie neben Android.mk für Ihr Testmodul angeben, wird sie automatisch in das BUILD_PACKAGE-Kern-Makefile aufgenommen.

Bevor Sie fortfahren, sollten Sie sich den Überblick über das App-Manifest ansehen.

Hier erhalten Sie eine Übersicht über die grundlegenden Komponenten einer Manifestdatei und ihre Funktionen.

Die aktuelle Version der Manifestdatei für die Beispiel-Gerrit-Änderung finden Sie unter: https://android.googlesource.com/platform/frameworks/base/+/main/packages/Shell/tests/AndroidManifest.xml

Hier ist ein Snapshot zur Veranschaulichung:

<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>

Einige ausgewählte Anmerkungen zur Manifestdatei:

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

Das Attribut package ist der Name des Anwendungspakets. Dies ist die eindeutige Kennung, mit der das Android-Anwendungsframework eine Anwendung (oder in diesem Kontext: Ihre Testanwendung) identifiziert. Jeder Nutzer im System kann nur eine Anwendung mit diesem Paketnamen installieren.

Da es sich um ein Test-App-Paket handelt, das unabhängig vom zu testenden App-Paket ist, muss ein anderer Paketname verwendet werden. Eine gängige Konvention ist das Hinzufügen des Suffixes .test.

Außerdem entspricht dieses package-Attribut dem Wert, der von ComponentName#getPackageName() zurückgegeben wird. Es ist auch das Attribut, das du verwendest, um über adb shell mit verschiedenen pm-Unterbefehlen zu interagieren.

Beachten Sie auch, dass der Paketname zwar normalerweise dem Stil eines Java-Paketnamens entspricht, aber nur wenig damit zu tun hat. Mit anderen Worten: Ihr Anwendungs- oder Testpaket kann Klassen mit beliebigen Paketnamen enthalten. Sie können aber auch den Java-Paketnamen der obersten Ebene in Ihrer Anwendung oder Ihrem Test mit dem Paketnamen der Anwendung identisch machen.

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

Dies ist für alle Instrumentierungstests erforderlich, da die zugehörigen Klassen in einer separaten JAR-Bibliotheksdatei des Frameworks verpackt sind. Daher sind zusätzliche Klassenpfad-Einträge erforderlich, wenn das Testpaket vom Anwendungsframework aufgerufen wird.

android:targetPackage="com.android.shell"

Dadurch wird das Zielpaket der Instrumentierung auf com.android.shell festgelegt. Wenn die Instrumentierung über den Befehl am instrument aufgerufen wird, startet das Framework den com.android.shell-Prozess neu und fügt dem Prozess für die Testausführung Instrumentierungscode hinzu. Das bedeutet auch, dass der Testcode Zugriff auf alle Klasseninstanzen hat, die in der zu testenden Anwendung ausgeführt werden, und je nach freigegebenen Test-Hooks den Status manipulieren kann.

Einfache Konfigurationsdatei

Jedes neue Testmodul muss eine Konfigurationsdatei haben, um das Buildsystem mit Modulmetadaten, Abhängigkeiten zur Kompilierungszeit und Verpackungsanweisungen zu steuern. In den meisten Fällen reicht die Soong-basierte Blueprint-Datei aus. Weitere Informationen finden Sie unter Einfache Testkonfiguration.

Komplexe Konfigurationsdatei

Für komplexere Tests müssen Sie auch eine Testkonfigurationsdatei für den Test-Harness von Android, Trade Federation, schreiben.

In der Testkonfiguration können spezielle Optionen für die Geräteeinrichtung und Standardargumente für die Testklasse angegeben werden.

Die neueste Version der Konfigurationsdatei für die Beispiel-Gerrit-Änderung finden Sie unter: frameworks/base/packages/Shell/tests/AndroidTest.xml

Hier ist ein Snapshot zur Veranschaulichung:

<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>

Einige ausgewählte Anmerkungen zur Testkonfigurationsdatei:

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

Dadurch wird Trade Federation angewiesen, die ShellTests.apk mit einem bestimmten target_preparer auf dem Zielgerät zu installieren. In Trade Federation stehen Entwicklern viele Zielvorbereitungstools zur Verfügung, mit denen sie dafür sorgen können, dass das Gerät vor der Testausführung richtig eingerichtet ist.

<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>

Hier wird die Trade Federation-Testklasse für die Ausführung des Tests angegeben. Außerdem werden das Paket auf dem Gerät, das ausgeführt werden soll, und das Test-Runner-Framework übergeben, in diesem Fall JUnit.

Weitere Informationen zu Testmodulkonfigurationen

JUnit4-Funktionen

Die Verwendung der android-support-test-Bibliothek als Test-Runner ermöglicht die Verwendung neuer JUnit4-Testklassen. Die Beispieländerung in Gerrit enthält einige sehr grundlegende Verwendungen ihrer Funktionen.

Den aktuellen Quellcode für die Beispieländerung in Gerrit finden Sie unter: frameworks/base/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java

Testmuster sind in der Regel spezifisch für Komponententeams, aber es gibt einige allgemein nützliche Nutzungsmuster.

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

Ein wesentlicher Unterschied bei JUnit 4 besteht darin, dass Tests nicht mehr von einer gemeinsamen Basistestklasse erben müssen. Stattdessen schreiben Sie Tests in einfachen Java-Klassen und verwenden Anmerkungen, um bestimmte Testkonfigurationen und -einschränkungen anzugeben. In diesem Beispiel wird angeordnet, dass diese Klasse als Android-JUnit4-Test ausgeführt werden soll.

Mit der @SmallTest-Anmerkung wurde eine Testgröße für die gesamte Testklasse angegeben: Alle Testmethoden, die dieser Testklasse hinzugefügt werden, übernehmen diese Anmerkung zur Testgröße. Vorbereitung der Testklasse, Aufräumen nach dem Test und Aufräumen nach dem Test der Klasse: ähnelt den Methoden setUp und tearDown in JUnit4. Die Test-Anmerkung wird für den eigentlichen Test verwendet.

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

Die @Before-Anmerkung wird von JUnit4 auf Methoden angewendet, um die Vorabeinrichtung für den Test durchzuführen. In diesem Beispiel wird @After nicht verwendet, aber es gibt auch eine Option für den Abbau nach dem Test. Ebenso können die Anmerkungen @BeforeClass und @AfterClass von JUnit4 auf Methoden angewendet werden, um vor der Ausführung aller Tests in einer Testklasse die Einrichtung und danach die Deaktivierung durchzuführen. Die Einrichtungs- und Deaktivierungsmethoden auf Klassenebene müssen statisch sein.

Anders als in früheren Versionen von JUnit muss der Methodenname bei den Testmethoden nicht mehr mit test beginnen. Stattdessen muss jede Methode mit @Test annotiert werden. Wie gewohnt müssen Testmethoden öffentlich sein, keinen Rückgabewert deklarieren, keine Parameter annehmen und dürfen Ausnahmen auslösen.

        Context context = InstrumentationRegistry.getTargetContext();

Da für JUnit4-Tests keine gemeinsame Basisklasse mehr erforderlich ist, müssen Context-Instanzen nicht mehr über getContext() oder getTargetContext() über Basisklassenmethoden abgerufen werden. Stattdessen werden sie vom neuen Test-Runner über InstrumentationRegistry verwaltet, wo die vom Instrumentierungs-Framework erstellte Kontext- und Umgebungseinrichtung gespeichert wird. Über diesen Kurs können Sie auch Folgendes aufrufen:

  • getInstrumentation(): die Instanz der Klasse Instrumentation
  • getArguments(): die Befehlszeilenargumente, die über -e <key> <value> an am instrument übergeben werden

Lokal erstellen und testen

Verwenden Sie für die häufigsten Anwendungsfälle Atest.

Bei komplexeren Fällen, die eine umfangreichere Anpassung erfordern, folgen Sie der Anleitung zur Instrumentierung.