Exemple de tests d'auto-instrumentation

Lorsqu'un test d'instrumentation est démarré, son package cible est redémarré avec du code d'instrumentation injecté et lancé pour l'exécution. Une exception est que le package cible ici ne peut pas être le framework d'application Android lui-même, tel que le package android, car cela entraînerait une situation paradoxale où le framework Android devrait être redémarré, ce qui est ce qui prend en charge les fonctions système, y compris l'instrumentation elle-même.

Cela signifie qu'un test d'instrumentation ne peut pas s'injecter dans Android (ou serveur système) pour exécution. Pour tester le framework Android, le code de test ne peut appeler que des surfaces d'API publiques ou celles exposées à l'aide du langage de définition d'interface Android AIDL disponible dans l'arborescence source de la plate-forme. Pour cette catégorie de tests, il n'est pas pertinent de cibler un package en particulier. Par conséquent, il est de coutume d'instrumentation à déclarer pour cibler son propre package d'application de test, défini dans sa propre balise <manifest> de AndroidManifest.xml.

Selon les exigences, les packages d'application de test de cette catégorie peuvent également :

  • Regroupez les activités nécessaires aux tests.
  • Partagez l'ID utilisateur avec le système.
  • être signées avec la clé de plate-forme ;
  • être compilé avec la source du framework plutôt qu'avec le SDK public ;

Cette catégorie de tests d'instrumentation est parfois appelée "auto-instrumentation". Voici quelques exemples de tests d'auto-instrumentation la source de la plate-forme:

L'exemple présenté ici consiste à écrire un nouveau test d'instrumentation avec dans son propre package d'application de test. Ce guide utilise les éléments suivants : à titre d'exemple:

Nous vous recommandons de commencer par parcourir le code pour obtenir une impression approximative avant de continuer.

Déterminer un emplacement source

En règle générale, votre équipe a déjà établi un schéma de vérification du code et d'ajout de tests. La plupart des équipes possèdent un seul dépôt Git. partagez-en un avec d'autres équipes, mais avez un sous-répertoire dédié qui contient le code source du composant.

En supposant que l'emplacement racine de la source de votre composant se trouve sur <component source root>, la plupart des composants comportent des dossiers src et tests, et certains des fichiers supplémentaires tels que Android.mk (ou divisés en fichiers .mk supplémentaires) ; le fichier manifeste AndroidManifest.xml et le fichier de configuration de test "AndroidTest.xml".

Étant donné que vous ajoutez un tout nouveau test, vous devrez probablement créer le tests à côté de votre composant src, puis remplissez-le avec du contenu.

Dans certains cas, votre équipe peut disposer d'autres structures de répertoires sous tests. en raison de la nécessité d'empaqueter différentes suites de tests dans des APK individuels. Et Dans ce cas, vous devez créer un sous-répertoire sous tests.

Quelle que soit la structure, vous finirez par remplir le répertoire tests ou le sous-répertoire nouvellement créé avec des fichiers similaires à ce qu'il y a dans instrumentation dans l'exemple de modification de gerrit. Le détail de chaque sont expliqués plus loin dans ce document.

Fichier manifeste

Comme pour un projet d'application, chaque module de test d'instrumentation nécessite fichier manifeste nommé AndroidManifest.xml. Pour inclure automatiquement à l'aide du fichier makefile principalBUILD_PACKAGE, fournissez-le à côté de Android.mk pour votre module de test.

Si vous ne connaissez pas bien le fichier AndroidManifest.xml, consultez Présentation du fichier manifeste d'application

Voici un exemple de fichier AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  android:sharedUserId="android.uid.system"
  package="android.test.example.helloworld" >

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

    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                     android:targetPackage="android.test.example.helloworld"
                     android:label="Hello World Test"/>

</manifest>

Quelques remarques concernant le fichier manifeste:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="android.test.example.helloworld" >

L'attribut package correspond au nom du package de l'application: il s'agit de l'attribut que le framework d'application Android utilise pour identifier application (ou, dans ce contexte, votre application de test). Chaque utilisateur du système ne peut installer qu'une seule application avec ce nom de package.

De plus, cet attribut package est identique à ce que vous ComponentName#getPackageName() ainsi que pour interagir avec différents sous-ensembles pm utilisent adb shell.

Notez que, bien que le nom du package soit généralement de même style qu'un nom de package Java, il n'a en réalité que très peu à voir avec celui-ci. Dans d'autres votre package d'application (ou de test) peut contenir des classes avec n'importe quel package de noms de domaine. D'un autre côté, vous pouvez opter pour la simplicité et faire en sorte du package Java du niveau de service dans votre application ou test identique à l'application nom du package.

android:sharedUserId="android.uid.system"

Il déclare qu'au moment de l'installation, ce fichier APK doit disposer du rôle le même ID utilisateur, c'est-à-dire l'identité d'exécution, que la plate-forme principale. Notez qu'il s'agit dépend de la signature de l'APK avec le même certificat que celui de la plate-forme principale. (voir LOCAL_CERTIFICATE dans une section précédente), mais elles sont différentes. concepts:

  • certaines autorisations ou API sont protégées par une signature, ce qui nécessite certificat de signature
  • Certaines autorisations ou API nécessitent l'identité utilisateur system de l'appelant, ce qui nécessite que le package appelant partage l'ID utilisateur avec system, s'il s'agit d'un package distinct de la plate-forme principale.
<uses-library android:name="android.test.runner" />

Cela est obligatoire pour tous les tests d'instrumentation, car les classes associées sont empaquetés dans un fichier de bibliothèque JAR de framework distinct, nécessite donc Entrées classpath lorsque le package de test est appelé par le framework d'application.

android:targetPackage="android.test.example.helloworld"

Vous avez peut-être remarqué que targetPackage ici est déclaré de la même manière que L'attribut package a été déclaré dans la balise manifest de ce fichier. Comme indiqué dans principes de base des tests, cette catégorie de tests d'instrumentation sert généralement à tester les API de framework. Il n'est donc pas très utile d’avoir un package d’application ciblé spécifique, autre que lui-même.

Fichier de configuration simple

Chaque nouveau module de test doit disposer d'un fichier de configuration le système de compilation avec les métadonnées du module, les dépendances au moment de la compilation et le packaging instructions. Dans la plupart des cas, l'option de fichier de plan basée sur Soong est suffisante. Pour en savoir plus, consultez Configuration de test simple.

Fichier de configuration complexe

Pour ces cas plus complexes, vous devez également écrire une configuration de test pour l'outil de test d'Android, Fédération de commerce.

La configuration de test peut spécifier des options de configuration d'appareil spéciales et des paramètres par défaut pour fournir la classe de test. Consultez l'exemple à l'adresse /platform_testing/tests/example/instrumentation/AndroidTest.xml.

Un instantané est inclus ici pour plus de commodité:

<configuration description="Runs sample instrumentation test.">
  <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
  <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
    <option name="test-file-name" value="HelloWorldTests.apk"/>
  </target_preparer>
  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
  <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
  <option name="test-suite-tag" value="apct"/>
  <option name="test-tag" value="SampleInstrumentationTest"/>

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

Remarques importantes concernant le fichier de configuration du test:

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

Cela indique à la fédération commerciale d'installer le HelloWorldTests.apk sur la cible appareil à l'aide d'un target_preparer spécifié. Il existe de nombreux préparateurs cibles à la disposition des développeurs de la Fédération commerciale l'appareil est correctement configuré avant l'exécution du test.

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

Cela spécifie la classe de test de la fédération de commerce à utiliser pour exécuter le test et transmet le package sur l'appareil à exécuter et le lanceur de test qui est JUnit dans ce cas.

Pour en savoir plus, consultez la section Configurations de test du module.

Fonctionnalités de JUnit4

L'utilisation de la bibliothèque android-support-test en tant qu'exécuteur de test permet d'adopter de nouvelles à des classes de test de style JUnit4, et l'exemple de modification de gerrit contient des éléments de ses fonctionnalités. Consultez l'exemple sur la page /platform_testing/tests/example/instrumentation/src/android/test/example/helloworld/HelloWorldTest.java.

Bien que les modèles de test soient généralement spécifiques aux équipes de composants, il existe des modèles d'utilisation généralement utiles.

@RunWith(JUnit4.class)
public class HelloWorldTest {

Une différence significative dans JUnit4 est que les tests ne sont plus nécessaires pour hériter d'une classe de test de base commune ; vous écrivez les tests en langage Java et utilisent des annotations pour indiquer certaines contraintes et configuration de test. Dans Dans cet exemple, nous indiquons que cette classe doit être exécutée en tant que test JUnit4.

    @BeforeClass
    public static void beforeClass() {
    ...
    @AfterClass
    public static void afterClass() {
    ...
    @Before
    public void before() {
    ...
    @After
    public void after() {
    ...
    @Test
    @SmallTest
    public void testHelloWorld() {
    ...

Les annotations @Before et @After sont utilisées sur les méthodes par JUnit4 pour effectuer la configuration avant et après le test. De même, @BeforeClass et Les annotations @AfterClass sont utilisées sur les méthodes de JUnit4 pour effectuer la configuration avant exécuter tous les tests d'une classe de test, puis les supprimer. Notez que les méthodes de configuration et de suppression à l'échelle de la classe doivent être statiques. Contrairement aux versions précédentes de JUnit, les méthodes de test ne doivent plus commencer par test. Au lieu de cela, chacune d'elles doit être annotée avec @Test. Comme d'habitude, les méthodes de test doivent être publiques, ne déclarer aucune valeur renvoyée, n'accepter aucun paramètre ; peut générer des exceptions.

Accès à la classe d'instrumentation

Bien qu'il ne soit pas abordé dans l'exemple de base "Hello World", il est assez courant pour une Test Android nécessitant l'accès à l'instance Instrumentation: il s'agit de l'API principale permettant d'accéder aux contextes d'application, au cycle de vie de l'activité les API de test associées, etc.

Comme les tests JUnit4 ne nécessitent plus de classe de base commune, il n'est plus nécessaire nécessaire pour obtenir l'instance Instrumentation via InstrumentationTestCase#getInstrumentation(), à la place, le nouveau lanceur de test le gère via InstrumentationRegistry. où la configuration contextuelle et environnementale créée par le framework d'instrumentation stockées.

Pour accéder à l'instance de la classe Instrumentation, il suffit d'appeler la méthode statique. getInstrumentation() sur InstrumentationRegistry:

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

Compiler et tester en local

Pour les cas d'utilisation les plus courants, utilisez Confirmer :

Pour les cas plus complexes nécessitant une personnalisation plus importante, suivez les instructions d'instrumentation.