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 preferiti. |
---|---|
Simulazione | Un doppio di test che si comporta come lo programmi e che ha aspettative sulle sue interazioni. Le simulazioni non supereranno i test se le loro interazioni non corrispondono ai requisiti da te 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à, i falsi sono da preferire 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 dei clic. |
Spionaggio | Un wrapper su un oggetto reale che tiene traccia anche di alcune informazioni aggiuntive, come nelle simulazioni. In genere vengono evitati per non aggiungere complessità. Le falsità o le truffe sono quindi da preferire alle spie. |
Ombre | Fake utilizzato in Robolectric. |
Esempio di utilizzo di un falso
Supponiamo di voler eseguire il test delle unità per un ViewModel che dipende da un'interfaccia
denominata UserRepository
ed espone il nome del primo utente a una UI. 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 UserRepository
fittizio non deve dipendere dalle origini dati locali e remote
utilizzate dalla versione di produzione. Il file si trova nel set di origine
del 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 delle unità, perché il valore 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 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 test doppi, ad esempio un test della UI strumentato che esplora un flusso utente completo nella tua app. In questo caso, potresti decidere di rendere il test ermetico. Un test ermetico evita tutte le dipendenze esterne, ad esempio il recupero dei dati da internet. Questo migliora 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.