In questa lezione su Android, proseguiremo lo studio di Espresso come meccanismo di test per le interfacce utente. Ne abbiamo già visto i principi fondamentali, mentre adesso aggiungeremo qualche funzionalità alla nostra app di prova, in modo da poter sperimentare ulteriormene questo framework.
Cosa sottoporremo a test
L'app da collaudare ha un'Activity principale che contiene una ListView
in cui vengono mostrati i dati dei soggetti da registrare. Le due funzionalità che metteremo sotto osservazione grazie ad Espresso sono:
- l'apertura di una finestra di dialogo, al click del Floating Action Button, che offrirà un form per l'inserimento dei dati di un nuovo soggetto;
- l'apertura di una nuova Activity al click di una singola riga della
ListView
, al fine di mostrare una scheda riepilogativa di un singolo soggetto registrato;
Attiveremo tramite Espresso entrambe queste funzionalità, e ne valuteremo gli effetti.
Primo test: inserimento tramite form
Considerando che il form su cui dobbiamo svolgere il test si trova in una finestra di dialogo, possiamo innanzitutto verificare che il Floating Action Button sia correttamente associato ad un'azione che ne inneschi effettivamente l'apertura. Il seguente breve test verifica proprio questo: produciamo un click sul pulsante flottante, e controlliamo se risulta visibile il layout che compone l'interfaccia utente del form al quale abbiamo assegnato esplicitamente un ID (R.id.dialog_layout
):
@Test
public void openDialog()
{
onView(withId(R.id.fab)).perform(click());
onView(withId(R.id.dialog_layout)).check(matches(isDisplayed()));
}
Potremo innestare questo test nella struttura di classe vista nella lezione precedente.
Il test più completo che abbiamo intenzione di svolgere si articola invece su più fasi:
- click sul Floating Action Button, come abbiamo visto poco fa;
- inserimento di testo all'interno delle
EditText
tramite il metodotypeText
; - salvataggio dei dati mediante pressione sul pulsante Salva;
- verifica dell'avvenuto inserimento leggendo quanto mostrato nella
TextView
presente all'ultima riga dellaListView
: se tutto è andato a buon fine, vi dovremmo ritrovare proprio i testi che abbiamo appena passato.
@Test
public void insertData()
{
// 1. le stringhe che inseriremo nel form
String nomePerTest="Enrico";
String cognomePerTest="Bianchi";
String etaPerTest="28";
// 2. cosa ci aspettiamo di leggere nell'ultima riga della ListView
String stringaAttesa=cognomePerTest+" "+nomePerTest+" - età "+etaPerTest;
// 3. click sul FAB
onView(withId(R.id.fab)).perform(click());
// 4. inserimento testi nel form
onView(withId(R.id.nome)).perform(typeText(nomePerTest));
onView(withId(R.id.cognome)).perform(typeText(cognomePerTest));
onView(withId(R.id.eta)).perform(typeText(etaPerTest));
// 5. salvataggio mediante click su pulsante "Salva"
onView(withId(android.R.id.button1)).perform(click());
// 6. leggiamo la nuova dimensione della ListView (dovrebbe essere aumentata)
int quanti=((ListView)mIntentActivityRule.getActivity().findViewById(R.id.listView)).getCount();
// 7. verifichiamo se ciò che c'è scritto nell'ultima riga della ListView equivale a stringaAttesa
onData(anything()).inAdapterView(withId(R.id.listView))
.atPosition(quanti-1)
.onChildView(withId(R.id.testo))
.check(matches(withText(containsString(stringaAttesa))));
}
Il test lavora interamente sull'interfaccia utente, sia a livello di inserimento sia di verifica, ma coinvolge i funzionamenti interni dell'Adapter, risultando pertanto piuttosto completo.
Secondo test: invocazione di Intent
Al click di una riga della ListView viene aperta una seconda Activity che mostra i dati relativi ad un soggetto: tali informazioni dovranno essere associate all'Intent. Espresso permette di svolgere diversi test sul funzionamento di un Intent, e qui ne proveremo alcuni. Per fare ciò però serve, per prima cosa, inserire un'ulteriore libreria tra le dipendenze, senza dimenticare di sincronizzare il progetto con i file di configurazione di Gradle:
dependencies {
...
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
...
}
Fatto questo, potremo utilizzare apposite classi per il testing. Osserviamo prima come funziona, nell'app di prova, il passaggio alla seconda Activity. Il layout che utilizziamo per rappresentare la struttura della riga della ListView
ha un attributo onClick
che indica quale metodo chiamare quando si esegue un tap su una riga:
<RelativeLayout
...
android:onClick="mostraDettagli">
All'interno della classe MainActivity, inoltre, troviamo il codice che attiva l'Intent (non prima di aver recuperato l'oggetto di classe Persona relativo alla riga selezionata, e averlo allegato al pacchetto di Extras). Si noti che, per semplicità, si è definita la classe Persona come implementazione di Serializable
:
public class MainActivity extends AppCompatActivity {
...
public final static String PERSONA_EXTRA="persona_extra"
...
public void mostraDettagli(View v)
{
int pos=listView.getPositionForView(v);
Persona p=adapter.getItem(pos);
Intent i=new Intent(this, DetailActivity.class);
i.putExtra(PERSONA_EXTRA, (Serializable) p);
startActivity(i);
}
...
}
All'interno dei test, dovremo:
- definire una regola sfruttando la classe IntentsTestRule, estensione di
ActivityTestRule
(già vista nella lezione precedente), per inizializzare il meccanismo di verifica degli Intent di Espresso prima dell'attivazione dei metodi contrassegnati con l'annotation@Test
; - eseguire la cosiddetta Intent Verification, all'interno dei metodi adibiti ai test, sfruttando il metodo
intended
che applicherà Matcher agli Intent rilevati. I Matcher da utilizzare saranno di classeIntentMatchers
.
Vediamo subito un esempio:
public class Intent_Test {
@Rule
public IntentsTestRule<MainActivity> mIntentActivityRule = new IntentsTestRule<>(MainActivity.class);
@Test
public void openNewActivity()
{
onData(anything())
.inAdapterView(withId(R.id.listView))
.atPosition(0)
.perform(click());
intended(toPackage("it.html.esempio_espresso02"));
}
}
Con la prima riga, viene eseguito un click sul primo elemento della ListView (che daremo per scontato esista), e successivamente con intended
eseguiamo il controllo verificando che l'Activity attivata faccia parte del package it.html.esempio_espresso02.
Altro metodo utile consiste nella verifica della presenza di un Extra caratterizzato da una determinata chiave:
@Test
public void checkExtra()
{
onData(anything())
.inAdapterView(withId(R.id.listView))
.atPosition(0)
.perform(click());
intended(hasExtraWithKey(MainActivity.PERSONA_EXTRA));
}
Note conclusive
Come si può immaginare, gli strumenti a disposizione di Espresso sono molti di più di quelli visti in queste lezioni, ma abbiamo comunque implementato i principali meccanismi di base per i test più comuni. Per tutto il resto, si può consultare la documentazione allegata al framework e alle varie librerie in ecco incluse. Si ricordi sempre l'utilità di poter lanciare anche test singoli: sarà sufficiente fare click con il tasto destro del mouse all'interno di un metodo e selezionare il relativo comando Run.
Inoltre, si cerchi sempre di verificare la bontà di un test non accontentandosi del suo successo, ma verificando che esso fallisca con dati volutamente errati.