Beispiel für die Ausrichtung auf eine App

Diese Kategorie von Instrumentierungstests unterscheidet sich nicht wesentlich von Tests, 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 über einige Kenntnisse im Workflow des Plattformquellbaums verfügen. Falls nicht, lesen Sie die Anforderungen. Im hier behandelten Beispiel wird ein neuer Instrumentierungstest geschrieben, bei dem das Zielpaket auf das eigene Testanwendungspaket festgelegt ist. Wenn Sie mit dem Konzept nicht vertraut sind, lesen Sie die Einführung in das Plattformtesting.

In diesem Leitfaden wird der folgende Test als Beispiel verwendet:

  • frameworks/base/packages/Shell/tests

Wir empfehlen, sich zuerst den Code anzusehen, um einen groben Eindruck zu erhalten, bevor Sie fortfahren.

Quellort festlegen

Da der Instrumentierungstest auf eine Anwendung ausgerichtet ist, wird der Testquellcode üblicherweise in einem tests-Verzeichnis unter dem Stammverzeichnis des Quellverzeichnisses Ihrer Komponente im Plattformquellbaum platziert.

Weitere Informationen zum Quellort finden Sie im End-to-End-Beispiel für selbstinstrumentierende Tests.

Manifestdatei

Wie eine reguläre Anwendung benötigt auch jedes Instrumentierungstestmodul eine Manifestdatei. Wenn Sie die Datei als AndroidManifest.xml benennen und sie neben Android.mk für Ihr Testmodul bereitstellen, wird sie automatisch von der BUILD_PACKAGE-Makefile-Datei des Kerns eingebunden.

Bevor Sie fortfahren, empfehlen wir Ihnen dringend, zuerst die Übersicht über App-Manifeste zu lesen.

Dort finden Sie eine Übersicht über die grundlegenden Komponenten einer Manifestdatei und ihre Funktionen.

Die aktuelle Version der Manifestdatei für die Beispieländerung in Gerrit finden Sie unter https://android.googlesource.com/platform/frameworks/base/+/android17-release/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 Paketname der Anwendung. Dies ist die eindeutige ID, die das Android-Anwendungsframework verwendet, um eine Anwendung zu identifizieren (oder in diesem Kontext: Ihre Testanwendung). Jeder Nutzer im System kann nur eine Anwendung mit diesem Paketnamen installieren.

Da es sich um ein Testanwendungspaket handelt, das unabhängig vom zu testenden Anwendungspaket ist, muss ein anderer Paketname verwendet werden. Eine gängige Konvention ist, das Suffix .test hinzuzufügen.

Außerdem entspricht dieses package-Attribut dem, was ComponentName#getPackageName() zurückgibt, und auch dem, was Sie verwenden würden, um über adb shell mit verschiedenen pm-Unter befehlen zu interagieren.

Beachten Sie auch, dass der Paketname zwar normalerweise im gleichen Stil wie ein Java-Paketname ist, aber tatsächlich nur sehr wenig damit zu tun hat. Mit anderen Worten: Ihr Anwendungs- oder Testpaket kann Klassen mit beliebigen Paketnamen enthalten. Andererseits können Sie es sich auch einfach machen und 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 Framework-JAR-Bibliotheksdatei verpackt sind. Daher sind zusätzliche Klassenpfadeinträ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 Prozess com.android.shell neu und fügt Instrumentierungscode für die Testausführung in den Prozess ein. Das bedeutet auch, dass der Testcode Zugriff auf alle Klasseninstanzen hat, die in der zu testenden Anwendung ausgeführt werden, und den Status möglicherweise ändern kann, je nachdem, welche Test-Hooks verfügbar sind.

Einfache Konfigurationsdatei

Jedes neue Testmodul muss eine Konfigurationsdatei haben, um das Build-System mit Modulmetadaten, Kompilierzeitabhängigkeiten und Verpackungsanweisungen zu versorgen. In den meisten Fällen reicht die Soong-basierte Blueprint-Dateioption aus. Weitere Informationen finden Sie unter Einfache Testkonfiguration.

Komplexe Konfigurationsdatei

Für komplexere Tests müssen Sie auch eine Testkonfigurationsdatei für die Testharness 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 aktuelle Version der Konfigurationsdatei für die Beispieländerung in Gerrit 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 Datei „ShellTests.apk“ mit einem angegebenen „target_preparer“ auf dem Zielgerät zu installieren. Entwicklern stehen in Trade Federation viele Zielvorbereiter zur Verfügung, mit denen sichergestellt werden kann, 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>

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

Weitere Informationen zu Testmodulkonfigurationen

JUnit4-Funktionen

Durch die Verwendung der Bibliothek android-support-test als Test-Runner können neue Testklassen im JUnit4-Stil übernommen werden. Die Beispieländerung in Gerrit enthält einige sehr grundlegende Verwendungen der 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, es gibt aber auch einige allgemein nützliche Nutzungsmuster.

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

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

Die Annotation @SmallTest gibt eine Testgröße für die gesamte Testklasse an. Alle Testmethoden, die dieser Testklasse hinzugefügt werden, übernehmen diese Annotation für die Testgröße. Vorbereitung vor der Testklasse, Bereinigung nach dem Test und Bereinigung nach der Testklasse: ähnlich den Methoden setUp und tearDown in JUnit4. Die Annotation Test wird verwendet, um den eigentlichen Test zu annotieren.

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

Die Annotation @Before wird von JUnit4 für Methoden verwendet, um die Vorbereitung vor dem Test durchzuführen. Obwohl sie in diesem Beispiel nicht verwendet wird, gibt es auch @After für die Bereinigung nach dem Test. Ebenso können die Annotationen @BeforeClass und @AfterClass von JUnit4 für Methoden verwendet werden, um die Einrichtung vor der Ausführung aller Tests in einer Testklasse und die Bereinigung danach durchzuführen. Die Methoden für die Einrichtung und Bereinigung auf Klassenebene müssen statisch sein.

Im Gegensatz zu früheren Versionen von JUnit müssen die Testmethoden nicht mehr mit test beginnen. Stattdessen muss jede von ihnen mit @Test annotiert werden. Wie üblich müssen Testmethoden öffentlich sein, keinen Rückgabewert deklarieren, keine Parameter annehmen und können Ausnahmen auslösen.

        Context context = InstrumentationRegistry.getTargetContext();

Da für die JUnit4-Tests keine gemeinsame Basisklasse mehr erforderlich ist, ist es nicht mehr notwendig, Context Instanzen über getContext() oder getTargetContext() über Basisklassenmethoden abzurufen. Stattdessen werden sie vom neuen Test-Runner über InstrumentationRegistry verwaltet, wo die kontextbezogene und umgebungsbezogene Einrichtung gespeichert wird, die vom Instrumentierungsframework erstellt wurde. Über diese Klasse 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

Build lokal erstellen und testen

Für die häufigsten Anwendungsfälle verwenden Sie Atest.

Für komplexere Fälle, die eine stärkere Anpassung erfordern, folgen Sie der Anleitung zur Instrumentierung.