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, ovvero il pacchetto android
, perché così facendo porterebbe alla situazione paradossale in cui il framework Android dovrebbe essere riavviato, che è ciò che supporta le funzioni di sistema, inclusa la strumentazione si.
Ciò significa che un test di strumentazione non può inserirsi nel framework Android, ovvero il server di sistema, per l'esecuzione. Per testare il framework Android, il codice di test può richiamare solo superfici API pubbliche o quelle esposte tramite Android Interface Definition Language AIDL disponibile nell'albero dei sorgenti della piattaforma. Per questa categoria di test, non è significativo puntare a un particolare pacchetto. Pertanto, è consuetudine che tali strumentazioni vengano dichiarate come destinazione del proprio pacchetto dell'applicazione di test, come definito nel proprio tag <manifest>
di AndroidManifest.xml
.
A seconda dei requisiti, i pacchetti di applicazioni di test in questa categoria possono anche:
- Raggruppa le attività necessarie per il test.
- Condividere l'ID utente con il sistema.
- Essere firmato con la chiave della piattaforma.
- Essere compilato rispetto all'origine del framework anziché all'SDK pubblico.
Questa categoria di test di strumentazione viene talvolta definita autostrumentazione. Di seguito alcuni esempi di prove di autostrumentazione nel sorgente della piattaforma:
L'esempio trattato qui sta scrivendo un nuovo test di strumentazione con il pacchetto di destinazione impostato nel proprio pacchetto dell'applicazione di test. Questa guida utilizza il seguente test come esempio:
Si consiglia di sfogliare prima il codice per avere un'impressione approssimativa prima di procedere.
Decidere su una posizione di origine
In genere il tuo team avrà già uno schema stabilito di punti in cui archiviare il codice e luoghi in cui aggiungere i test. La maggior parte dei team possiede un singolo repository git o ne condivide uno con altri team, ma dispone di una sottodirectory dedicata che contiene il codice sorgente del componente.
Supponendo che la posizione principale per l'origine del componente sia <component source root>
, la maggior parte dei componenti contiene cartelle src
e tests
e alcuni file aggiuntivi come Android.mk
(o suddivisi in file .mk
aggiuntivi), il file manifest AndroidManifest.xml
e il file di configurazione del test "AndroidTest.xml".
Dato che stai aggiungendo un test nuovo di zecca, probabilmente dovrai creare la directory tests
accanto al tuo componente src
e popolarla con il contenuto.
In alcuni casi, il tuo team potrebbe avere ulteriori strutture di directory sotto tests
a causa della necessità di impacchettare diverse suite di test in singoli apk. E in questo caso, dovrai creare una nuova sottodirectory sotto tests
.
Indipendentemente dalla struttura, finirai per popolare la directory tests
o la sottodirectory appena creata con file simili a quelli presenti nella directory instrumentation
nella modifica gerrit di esempio. Le sezioni seguenti spiegheranno in ulteriori dettagli di ciascun file.
File manifesto
Proprio come una normale applicazione, ogni modulo di test di strumentazione necessita di un file manifest. Se chiami il file come AndroidManifest.xml
e lo fornisci accanto ad Android.mk
per il tuo modulo di test, verrà incluso automaticamente dal makefile principale BUILD_PACKAGE
.
Prima di procedere oltre, si consiglia vivamente di esaminare prima la panoramica del manifesto dell'app .
Questo fornisce una panoramica dei componenti di base di un file manifest e delle loro funzionalità. Vedere l'esempio in platform_testing/tests/example/instrumentation/AndroidManifest.xml .
Un'istantanea è inclusa qui per comodità:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.test.example.helloworld" >
<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: questo è l'identificatore univoco utilizzato dal framework dell'applicazione Android per identificare un'applicazione (o in questo contesto: l'applicazione di test). Ogni utente nel sistema può installare solo un'applicazione con quel nome di pacchetto.
Inoltre, questo attributo del package
è uguale a quello restituito da ComponentName#getPackageName()
e anche lo stesso che useresti per interagire con vari comandi secondari pm
tramite adb shell
.
Si noti inoltre che sebbene il nome del pacchetto sia tipicamente nello stesso stile di un nome di pacchetto Java, in realtà ha pochissime cose a che fare con esso. In altre parole, il pacchetto della tua applicazione (o test) può contenere classi con qualsiasi nome di pacchetto, anche se, d'altra parte, potresti optare per la semplicità e avere il nome del tuo pacchetto Java di primo livello nella tua applicazione o test identico al nome del pacchetto dell'applicazione.
android:sharedUserId="android.uid.system"
Ciò dichiara che al momento dell'installazione, a questo apk dovrebbe essere concesso lo stesso ID utente, ovvero l'identità di runtime, della piattaforma principale. Nota che questo dipende dal fatto che l'apk sia firmato con lo stesso certificato della piattaforma principale (vedi LOCAL_CERTIFICATE
nella sezione precedente), ma sono concetti diversi:
- alcune autorizzazioni o API sono protette da firma, che richiede lo stesso certificato di firma
- alcune autorizzazioni o API richiedono l'identità dell'utente di
system
del chiamante, che richiede che il pacchetto chiamante condivida l'ID utente consystem
, se si tratta di un pacchetto separato dalla piattaforma principale stessa
<uses-library android:name="android.test.runner" />
Questo è necessario per tutti i test di strumentazione poiché le classi correlate sono impacchettate in un file di libreria jar del framework separato, pertanto richiede voci aggiuntive del percorso di classe quando il pacchetto di test viene richiamato dal framework dell'applicazione.
android:targetPackage="android.test.example.helloworld"
Potresti aver notato che targetPackage
qui è dichiarato uguale all'attributo package
dichiarato nel tag manifest
di questo file. Come accennato nelle nozioni di base sui test , questa categoria di test di strumentazione è in genere destinata al test delle API del framework, quindi non è molto significativo per loro avere un pacchetto di applicazioni mirato specifico, diverso da se stesso.
File di configurazione semplice
Ogni nuovo modulo di test deve avere un file di configurazione per dirigere il sistema di compilazione con i metadati del modulo, le dipendenze in fase di compilazione e le istruzioni di impacchettamento. Nella maggior parte dei casi, l'opzione del file Blueprint basata su Soong è sufficiente. Per i dettagli, vedere Configurazione test semplice .
File di configurazione complesso
Per questi casi più complessi, devi anche scrivere un file di configurazione di test per il test harness di Android, Trade Federation .
La configurazione di test può specificare speciali opzioni di configurazione del dispositivo e argomenti predefiniti per fornire la classe di test. Vedere l'esempio in /platform_testing/tests/example/instrumentation/AndroidTest.xml .
Un'istantanea è inclusa qui per comodità:
<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>
Alcune osservazioni selezionate sul file di configurazione del test:
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="test-file-name" value="HelloWorldTests.apk"/>
</target_preparer>
Questo dice a Trade Federation di installare HelloWorldTests.apk sul dispositivo di destinazione utilizzando un target_preparer specificato. Ci sono molti preparatori target disponibili per gli sviluppatori in Trade Federation e questi 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>
Questo specifica la classe di test della Federazione commerciale da utilizzare per eseguire il test e passa nel pacchetto sul dispositivo da eseguire e il framework del test runner che in questo caso è JUnit.
Per ulteriori informazioni, consulta Test Module Configs .
Caratteristiche 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 del gerrit di esempio contiene alcuni usi molto basilari delle sue funzionalità. Vedere l'esempio in /platform_testing/tests/example/instrumentation/src/android/test/example/helloworld/HelloWorldTest.java .
Sebbene i modelli di test siano in genere specifici per i team di componenti, esistono alcuni modelli 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; invece, scrivi test in semplici classi Java e usi l'annotazione per indicare determinate impostazioni e vincoli di 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 sui metodi di JUnit4 per eseguire l'impostazione pre-test e lo smontaggio post-test. Allo stesso modo, le annotazioni @BeforeClass
e @AfterClass
vengono utilizzate sui metodi di JUnit4 per eseguire l'impostazione prima di eseguire tutti i test in una classe di test e successivamente lo smontaggio. Si noti che i metodi di installazione e smontaggio dell'ambito di classe devono essere statici. Per quanto riguarda i metodi di test, a differenza della versione precedente di JUnit, non è più necessario che il nome del metodo inizi con test
, ma ciascuno di essi deve essere annotato con @Test
. Come al solito, i metodi di test devono essere pubblici, non dichiarare alcun valore restituito, non accettare parametri e possono generare eccezioni.
Importante : i metodi di test stessi sono annotati con l'annotazione @Test
; e nota che per i test da eseguire tramite APCT, devono essere annotati con le dimensioni del test: il metodo annotato di esempio testHelloWorld
come @SmallTest
. L'annotazione può essere applicata nell'ambito del metodo o nell'ambito della classe.
Accesso alla instrumentation
Sebbene non sia trattato nell'esempio base di hello world, è abbastanza comune che un test Android richieda l'accesso all'istanza di Instrumentation
: questa è l'interfaccia API principale che fornisce l'accesso ai contesti dell'applicazione, alle API di test relative 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 di Instrumentation
tramite InstrumentationTestCase#getInstrumentation()
, invece, il nuovo test runner lo gestisce tramite InstrumentationRegistry
dove è memorizzata la configurazione contestuale e ambientale creata dal framework di strumentazione.
Per accedere all'istanza della classe Instrumentation
, è sufficiente chiamare il metodo statico getInstrumentation()
sulla classe InstrumentationRegistry
:
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()
Crea e testa in locale
Per i casi d'uso più comuni, utilizzare Atest .
Per casi più complessi che richiedono una personalizzazione più pesante, seguire le istruzioni sulla strumentazione .