Quando testi un elemento o un sistema di elementi, lo fai in isolamento. Ad esempio, per testare un ViewModel non è necessario avviare un emulatore e lanciare un'interfaccia utente perché non dipende (o non dovrebbe dipendere) dal framework Android.
Tuttavia, l'oggetto sottoposto a test potrebbe dipendere da altri per funzionare. Ad esempio, un ViewModel potrebbe dipendere da un repository di dati per funzionare.
Quando devi fornire una dipendenza a un soggetto sottoposto a test, una pratica comune è creare un doppio di test (o un oggetto di test). I sostituti di test sono oggetti che sembrano e si comportano come componenti della tua app, ma vengono creati nel test per fornire un comportamento o dati specifici. I vantaggi principali sono che rendono i test più rapidi e semplici.
Tipi di doppioni di test
Esistono vari tipi di doppioni di test:
Falso | Un doppio di test che ha un'implementazione "funzionante" della classe, ma è implementato in modo da essere adatto ai test, ma non alla produzione.
Esempio: un database in memoria. I falsi non richiedono un framework di simulazione e sono leggeri. Sono preferibili. |
---|---|
Simulazione | Un doppio di test che si comporta come lo programmi e che ha aspettative sulle sue interazioni. I test non andranno a buon fine se le interazioni dei mock non corrispondono ai requisiti definiti. In genere, i mock vengono creati con un framework di simulazione per ottenere tutto questo.
Esempio: verifica che un metodo in un database sia stato chiamato esattamente una volta. |
Stub | Un doppio di test che si comporta come lo programmi, ma non ha aspettative sulle sue interazioni. Di solito viene creato con un framework di simulazione. Per semplicità, è preferibile utilizzare i falsi rispetto agli stub. |
dummy | Un doppio di test che viene passato ma non utilizzato, ad esempio se devi solo fornirlo come parametro.
Esempio: una funzione vuota passata come callback per i clic. |
Spionaggio | Un wrapper su un oggetto reale che tiene traccia anche di alcune informazioni aggiuntive, in modo simile ai mock. In genere vengono evitati per non aggiungere complessità. Pertanto, è preferibile utilizzare fake o simulazioni rispetto alle spie. |
Ombre | Fake utilizzato in Robolectric. |
Esempio di utilizzo di un falso
Supponiamo che tu voglia eseguire il test di unità di un ViewModel che dipende da un'interfaccia chiamata UserRepository
ed esponga il nome del primo utente a un'interfaccia utente. Puoi creare un doppio test falso implementando l'interfaccia e restituendo dati noti.
object FakeUserRepository : UserRepository {
fun getUsers() = listOf(UserAlice, UserBob)
}
val const UserAlice = User("Alice")
val const UserBob = User("Bob")
Questo falso UserRepository
non deve dipendere dalle fonti di dati locali e remote utilizzate dalla versione di produzione. Il file si trova nel set di origine
di test e non verrà fornito con l'app di produzione.
Il seguente test verifica che ViewModel esponga correttamente il primo nome dell'utente alla vista.
@Test
fun viewModelA_loadsUsers_showsFirstUser() {
// Given a VM using fake data
val viewModel = ViewModelA(FakeUserRepository) // Kicks off data load on init
// Verify that the exposed data is correct
assertEquals(viewModel.firstUserName, UserAlice.name)
}
Sostituire UserRepository
con un falso è facile in un test unitario, perché il ViewModel viene creato dal tester. Tuttavia, può essere difficile sostituire elementi arbitrari in test più grandi.
Sostituzione dei componenti e iniezione di dipendenze
Quando i test non hanno alcun controllo sulla creazione dei sistemi in test, la sostituzione dei componenti con i sostituti di test diventa più complessa e richiede che l'architettura dell'app segua un design testabile.
Anche i test end-to-end di grandi dimensioni possono trarre vantaggio dall'utilizzo di sostituti di test, ad esempio un test dell'interfaccia utente instrumentata che naviga attraverso un flusso utente completo nella tua app. In questo caso, ti consigliamo di rendere il test ermetico. Un test ermetico evita tutte le dipendenze esterne, ad esempio il recupero dei dati da internet. Ciò consente di migliorare l'affidabilità e le prestazioni.
Puoi progettare la tua app per ottenere questa flessibilità manualmente, ma ti consigliamo di utilizzare un framework di iniezione di dipendenze come Hilt per sostituire i componenti della tua app al momento del test. Consulta la guida ai test di Hilt.
Passaggi successivi
La pagina Strategie di test mostra come puoi migliorare la produttività utilizzando diversi tipi di test.