Quando viene avviato un test di misurazione, il relativo pacchetto di destinazione viene riavviato con il codice di misurazione inserito e avviato per l'esecuzione. Un'eccezione è che il pacchetto di destinazione qui non può essere il framework di applicazione Android stesso, ad esempio il pacchetto android
, perché ciò porta alla situazione paradossale in cui il framework Android dovrebbe essere riavviato, che supporta le funzioni di sistema, inclusa la stessa strumentazione.
Ciò significa che un test di strumentazione non può iniettarsi nel framework Android, noto anche come 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 il linguaggio di definizione delle interfacce Android
AIDL
disponibile nella struttura della sorgente della piattaforma. Per questa categoria di test, non è significativo scegliere come target un determinato pacchetto. Pertanto, è consuetudine che queste misurazioni vengano dichiarate come target del 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 possono anche:
- Raggruppa le attività necessarie per i test.
- Condividi l'ID utente con il sistema.
- Essere firmate con la chiave della piattaforma.
- Essere compilato in base al codice sorgente del framework anziché all'SDK pubblico.
Questa categoria di test di misurazione è talvolta indicata come automisurazione. Ecco alcuni esempi di test di autostrumentazione nel codice sorgente della piattaforma:
L'esempio trattato qui riguarda la scrittura di un nuovo test di misurazione con il pacchetto di destinazione 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 approssimativa prima di procedere.
Scegli una posizione di origine
In genere, il tuo team avrà già stabilito una serie di punti di controllo nel codice e punti in cui aggiungere test. La maggior parte dei team possiede un'unica repository Git o ne condivide una con altri team, ma dispone di una sottodirectory dedicata che contiene il codice sorgente dei componenti.
Supponendo che la posizione principale dell'origine del componente sia <component source
root>
, la maggior parte dei componenti ha sotto le 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'.
Poiché stai aggiungendo un test nuovo di zecca, probabilmente dovrai creare la directory tests
accanto al componente src
e compilarla con i contenuti.
In alcuni casi, il tuo team potrebbe avere altre strutture di directory in tests
a causa della necessità di pacchettizzare diverse suite di test in singoli apk. In questo caso, dovrai creare una nuova sottodirectory in tests
.
Indipendentemente dalla struttura, finirai per compilare la directory tests
o la sottodirectory appena creata con file simili a quelli contenuti nella directory instrumentation
nella modifica di gerrit di esempio. I dettagli di ciascun
file sono spiegati più avanti in questo documento.
File manifest
Come per un progetto di app, ogni modulo di test di misurazione richiede un
file manifest denominato AndroidManifest.xml
. Per includere automaticamente questo file utilizzando il file makefile di base BUILD_PACKAGE
, forniscilo accanto al file Android.mk
per il modulo di test.
Se non hai dimestichezza con il file AndroidManifest.xml
, consulta la Panoramica del file 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>
Alcune note selezionate 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 utilizzato dal framework di applicazioni Android 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
è lo stesso restituito da ComponentName#getPackageName()
e anche quello che utilizzeresti per interagire con i vari comandi secondari pm
utilizzando adb shell
.
Tieni presente che, anche se il nome del pacchetto è in genere nello stesso stile di un nome di pacchetto Java, in realtà ha ben poco a che fare con esso. In altre parole, il pacchetto dell'applicazione (o del test) può contenere classi con qualsiasi nome del pacchetto, anche se, d'altra parte, puoi 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"
Dichiara che al momento dell'installazione a questo file APK deve essere concesso lo stesso ID utente, ovvero l'identità di runtime, della piattaforma di base. Tieni presente che questo dipende dalla firma dell'APK con lo stesso certificato della piattaforma di base (vedi LOCAL_CERTIFICATE
in una sezione precedente), ma si tratta di concetti diversi:
- alcune autorizzazioni o API sono protette da firma, il che richiede lo stesso certificato di firma
- alcune autorizzazioni o API richiedono l'identità utente
system
dell'utente chiamante, che richiede che il pacchetto chiamante condivida l'ID utente consystem
, se si tratta di un pacchetto separato dalla piattaforma di base stessa
<uses-library android:name="android.test.runner" />
Questo è necessario per tutti i test di strumentazione poiché le classi correlate sono imballate in un file della libreria JAR del framework separato, pertanto richiedono 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 come l'attributo package
dichiarato nel tag manifest
di questo file. Come accennato in Nozioni di base sui test, questa categoria di test di strumenti è in genere destinata al test delle API del framework, pertanto non è molto significativo che abbiano un pacchetto di applicazioni target 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 i metadati del modulo, le dipendenze in fase di compilazione e le istruzioni di imballaggio. Nella maggior parte dei casi, l'opzione del file Blueprint basato su Soong è sufficiente. Per maggiori dettagli, consulta Configurazione di un test semplice.
File di configurazione complesso
Per questi casi più complessi, devi anche scrivere un file di configurazione del test per la suite di test di Android, Trade Federation.
La configurazione del test può specificare opzioni di configurazione del dispositivo speciali e argomenti predefinite per fornire la classe di test. Consulta 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>
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 indica a Trade Federation di installare HelloWorldTests.apk sul dispositivo di destinazione utilizzando un target_preparer specificato. In Trade Federation sono disponibili molti preparatisti di target per gli sviluppatori, che possono essere utilizzati per assicurarsi 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 Trade Federation da utilizzare per eseguire il test e passa il pacchetto sul dispositivo da eseguire e il framework del programma di test, che in questo caso è JUnit.
Per ulteriori informazioni, consulta Test Config Moduli.
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 gerrit di esempio contiene alcuni utilizzi molto di base delle sue funzionalità. Guarda 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 di componenti, esistono alcuni pattern di utilizzo utili in generale.
@RunWith(JUnit4.class)
public class HelloWorldTest {
Una differenza significativa di JUnit4 è che i test non sono più tenuti a ereditare da una classe di test di base comune. Invece, scrivi i test in classi Java semplici e utilizzi le annotazioni per indicare determinate configurazioni e vincoli di test. In questo esempio, dichiariamo che questo corso deve essere eseguito 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 la configurazione pre test e il teardown post test. Analogamente, le annotazioni @BeforeClass
e
@AfterClass
vengono utilizzate sui metodi di JUnit4 per eseguire la configurazione prima
di eseguire tutti i test in un test class e il teardown successivamente. Tieni presente che i metodi di configurazione e smontaggio a livello di 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
, ma ogni metodo deve essere annotato con @Test
. Come di consueto,
i metodi di test devono essere pubblici, non dichiarare un valore restituito, non accettare parametri e
poter generare eccezioni.
Accesso alla classe di misurazione
Sebbene non sia trattato nell'esempio di base Ciao mondo, è abbastanza comune che un test Android richieda un'istanza di accesso Instrumentation
: si tratta dell'interfaccia API di base che fornisce l'accesso ai contesti dell'applicazione, alle API di test relative al ciclo di vita dell'attività e altro ancora.
Poiché i test JUnit4 non richiedono più una classe di base comune, non è più necessario ottenere un'istanza Instrumentation
tramite InstrumentationTestCase#getInstrumentation()
, ma il nuovo test runner la gestisce tramite InstrumentationRegistry
, dove viene memorizzata la configurazione contestuale e ambientale creata dal framework di misurazione.
Per accedere all'istanza della classe Instrumentation
, è sufficiente chiamare il metodo statico
getInstrumentation()
sulla classe InstrumentationRegistry
:
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()
Esegui la compilazione e i test in locale
Per i casi d'uso più comuni, utilizza Atest.
Per casi più complessi che richiedono una maggiore personalizzazione, segui le istruzioni di strumentazione.