Quando viene avviato un test di strumentazione, il relativo pacchetto di destinazione viene
riavviato con il codice di strumentazione inserito e avviato per l'esecuzione. Un'eccezione è che il pacchetto di destinazione qui non può essere il framework dell'applicazione Android stesso, ad esempio il pacchetto android
, perché ciò porta alla situazione paradossale in cui il framework Android dovrebbe essere riavviato, il che supporta le funzioni di sistema, inclusa l'instrumentation stessa.
Ciò significa che un test di instrumentazione non può essere inserito nel framework Android, ovvero nel server di sistema, per l'esecuzione. Per testare il framework Android, il codice di test può richiamare solo interfacce API pubbliche o quelle esposte utilizzando Android Interface Definition Language AIDL disponibili nell'albero dei sorgenti della piattaforma. Per questa categoria di test, non è significativo scegliere come target un pacchetto specifico. Pertanto, è consuetudine che queste
strumentazioni vengano dichiarate per avere come target il proprio pacchetto di applicazioni di test, come
definito nel proprio tag <manifest>
di AndroidManifest.xml
.
A seconda dei requisiti, i pacchetti di applicazioni di test in questa categoria potrebbero anche:
- Attività del bundle necessarie per il test.
- Condividi l'ID utente con il sistema.
- Essere firmato con la chiave della piattaforma.
- Essere compilato in base all'origine del framework anziché all'SDK pubblico.
Questa categoria di test di strumentazione è talvolta chiamata strumentazione automatica. Ecco alcuni esempi di test di autostrumentazione nel codice sorgente della piattaforma:
L'esempio trattato qui riguarda la scrittura di un nuovo test di strumentazione con il pacchetto target impostato sul proprio pacchetto dell'applicazione di test. Questa guida utilizza il seguente test come esempio:
Ti consigliamo di sfogliare prima il codice per farti un'idea generale prima di procedere.
Scegliere una posizione di origine
In genere, il tuo team avrà già un pattern consolidato di luoghi in cui controllare il codice e luoghi in cui aggiungere i test. La maggior parte dei team possiede un singolo repository Git oppure ne condivide uno con altri team, ma ha una sottodirectory dedicata che contiene il codice sorgente del componente.
Supponendo che la posizione principale dell'origine del componente sia <component source
root>
, la maggior parte dei componenti ha le cartelle src
e tests
al suo interno, oltre ad alcuni
file aggiuntivi come Android.mk
(o suddivisi in altri file .mk
),
il file manifest AndroidManifest.xml
e il file di configurazione del test
"AndroidTest.xml".
Poiché stai aggiungendo un test completamente nuovo, probabilmente dovrai creare la directory
tests
accanto al componente src
e popolarla con i contenuti.
In alcuni casi, il tuo team potrebbe avere ulteriori strutture di directory in tests
a causa della necessità di raggruppare diverse suite di test in APK individuali. In questo caso, dovrai creare una nuova sottodirectory in tests
.
Indipendentemente dalla struttura, alla fine la directory tests
o la sottodirectory appena creata verrà compilata con file simili a quelli della directory instrumentation
nella modifica di Gerrit di esempio. I dettagli di ogni file vengono spiegati più avanti in questo documento.
File manifest
Come per un progetto di app, ogni modulo di test di strumentazione richiede un file manifest denominato AndroidManifest.xml
. Per includere automaticamente
questo file utilizzando il makefile principaleBUILD_PACKAGE
, fornisci questo file accanto al file
Android.mk
per il modulo di test.
Se non hai familiarità con il file AndroidManifest.xml
, consulta la
panoramica del manifest dell'app.
Di seguito è riportato un file AndroidManifest.xml
di esempio:
<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>
Alcuni commenti selezionati sul file manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.test.example.helloworld" >
L'attributo package
è il nome del pacchetto dell'applicazione: si tratta dell'identificatore univoco che il framework dell'applicazione Android utilizza per identificare un'applicazione (o, in questo contesto, la tua applicazione di test). Ogni utente del sistema
può installare una sola applicazione con quel nome del pacchetto.
Inoltre, questo attributo package
è uguale a quello restituito da
ComponentName#getPackageName()
ed è lo stesso che utilizzeresti per interagire con vari sottocomandi pm
utilizza adb shell
.
Tieni presente che, sebbene il nome del pacchetto abbia in genere lo stesso stile di un nome di pacchetto Java, in realtà ha ben poco a che fare con quest'ultimo. In altre parole, il pacchetto dell'applicazione (o del test) può contenere classi con qualsiasi nome del pacchetto, anche se, d'altra parte, potresti optare per la semplicità e avere il nome del pacchetto Java di primo livello nell'applicazione o nel test identico al nome del pacchetto dell'applicazione.
android:sharedUserId="android.uid.system"
Ciò dichiara che al momento dell'installazione, a questo file APK deve essere concesso lo stesso ID utente, ovvero la stessa identità di runtime, della piattaforma principale. Tieni presente che questo dipende dal fatto che l'APK sia firmato con lo stesso certificato della piattaforma principale
(vedi LOCAL_CERTIFICATE
in una sezione precedente), ma si tratta di concetti diversi:
- alcune autorizzazioni o API sono protette dalla firma, il che richiede lo stesso certificato di firma
- alcune autorizzazioni o API richiedono l'identità utente
system
del chiamante, che richiede al pacchetto chiamante di condividere l'ID utente consystem
, se si tratta di un pacchetto separato dalla piattaforma principale
<uses-library android:name="android.test.runner" />
Questo è necessario per tutti i test di strumentazione, poiché le classi correlate sono inserite in un file JAR della libreria del framework separato, pertanto richiedono voci classpath aggiuntive quando il pacchetto di test viene richiamato dal framework dell'applicazione.
android:targetPackage="android.test.example.helloworld"
Potresti aver notato che targetPackage
qui è dichiarato come l'attributo package
dichiarato nel tag manifest
di questo file. Come indicato nelle
nozioni di base sui test, questa categoria di test di strumentazione è
in genere destinata alle API del framework di test, quindi non è molto significativo che
abbiano un pacchetto di applicazioni di destinazione specifico, diverso da se stesso.
File di configurazione semplice
Ogni nuovo modulo di test deve avere un file di configurazione per indirizzare il sistema di compilazione con metadati del modulo, dipendenze in fase di compilazione e istruzioni di packaging. Nella maggior parte dei casi, l'opzione di file Blueprint basata su Soong è sufficiente. Per maggiori dettagli, vedi Configurazione semplice del test.
File di configurazione complesso
Per questi casi più complessi, devi anche scrivere un file di configurazione di test per lo strumento di test di Android, Trade Federation.
La configurazione del test può specificare opzioni speciali di configurazione del dispositivo e argomenti predefiniti per fornire la classe di test. Vedi l'esempio in /platform_testing/tests/example/instrumentation/AndroidTest.xml.
Per comodità, è incluso uno snapshot:
<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>
Alcuni commenti selezionati sul file di configurazione del test:
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="test-file-name" value="HelloWorldTests.apk"/>
</target_preparer>
Indica a Trade Federation di installare HelloWorldTests.apk sul dispositivo di destinazione utilizzando un target_preparer specificato. In Trade Federation sono disponibili molti preparatori di target e possono essere utilizzati per garantire che il dispositivo sia configurato correttamente prima dell'esecuzione del 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>
Specifica la classe di test Trade Federation da utilizzare per eseguire il test e passa il pacchetto sul dispositivo da eseguire e il framework di esecuzione dei test che in questo caso è JUnit.
Per saperne di più, consulta Configurazioni dei moduli di test.
Funzionalità di JUnit4
L'utilizzo della libreria android-support-test
come test runner consente l'adozione di nuove
classi di test in stile JUnit4 e la modifica di esempio di Gerrit contiene alcuni utilizzi molto semplici
delle sue funzionalità. Vedi l'esempio in
/platform_testing/tests/example/instrumentation/src/android/test/example/helloworld/HelloWorldTest.java.
Sebbene i pattern di test siano in genere specifici per i team dei componenti, esistono alcuni pattern di utilizzo generalmente utili.
@RunWith(JUnit4.class)
public class HelloWorldTest {
Una differenza significativa in JUnit4 è che i test non devono più ereditare da una classe di test di base comune; al contrario, scrivi i test in classi Java semplici e utilizza le annotazioni per indicare determinate configurazioni e vincoli dei test. In questo esempio, stiamo indicando che questa classe deve essere eseguita come 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() {
...
Le annotazioni @Before
e @After
vengono utilizzate nei metodi di JUnit4 per eseguire
la configurazione pre-test e l'eliminazione post-test. Analogamente, le annotazioni @BeforeClass
e
@AfterClass
vengono utilizzate nei metodi da JUnit4 per eseguire la configurazione prima
di eseguire tutti i test in una classe di test e la pulizia successiva. Tieni presente che i metodi
di configurazione e pulizia dell'ambito della classe devono essere statici. Per quanto riguarda i metodi di test,
a differenza delle versioni precedenti di JUnit, non è più necessario iniziare il nome del metodo
con test
. Ogni metodo deve invece essere annotato con @Test
. Come di consueto,
i metodi di test devono essere pubblici, non dichiarare alcun valore restituito, non accettare parametri e
possono generare eccezioni.
Accesso alla classe di strumentazione
Sebbene non sia trattato nell'esempio di base Hello World, è abbastanza comune che un test Android richieda l'accesso all'istanza Instrumentation
: questa è l'interfaccia API principale che fornisce l'accesso ai contesti delle applicazioni, alle API di test correlate al ciclo di vita delle attività e altro ancora.
Poiché i test JUnit4 non richiedono più una classe base comune, non è più
necessario ottenere l'istanza Instrumentation
tramite
InstrumentationTestCase#getInstrumentation()
. Il nuovo test runner
la gestisce tramite InstrumentationRegistry
,
dove vengono archiviate la configurazione contestuale e ambientale creata dal framework di strumentazione.
Per accedere all'istanza della classe Instrumentation
, chiama semplicemente il metodo statico
getInstrumentation()
sulla classe InstrumentationRegistry
:
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()
Creare build e testare in locale
Per i casi d'uso più comuni, utilizza Atest.
Per i casi più complessi che richiedono una personalizzazione più approfondita, segui le istruzioni di strumentazione.