Un test locale viene eseguito direttamente sulla tua workstation, anziché su un dispositivo o emulatore Android. In questo modo, utilizza la Java Virtual Machine (JVM) locale, anziché un dispositivo Android, per eseguire i test. I test locali ti consentono di valutare la logica della tua app più rapidamente. Tuttavia, l'impossibilità di interagire con il framework Android crea una limitazione nei tipi di test che puoi eseguire.
Un test delle unità verifica il comportamento di una piccola sezione di codice, l'unità in fase di test. Per farlo, esegue il codice e controlla il risultato.
I test delle unità sono solitamente semplici, ma la loro configurazione può essere problematica quando l'unità in fase di test non è progettata tenendo conto della testabilità:
- Il codice che vuoi verificare deve essere accessibile da un test. Ad esempio, non puoi testare direttamente un metodo privato. Invece, testi la classe utilizzando le sue API pubbliche.
- Per eseguire i test delle unità in isolamento, le dipendenze dell'unità in fase di test devono essere sostituite da componenti che controlli, come falsi o altre controfigure di test. Questo è particolarmente problematico se il codice dipende dal framework Android.
Per scoprire di più sulle strategie di test delle unità comuni in Android, leggi Cosa testare.
Località dei test locali
Per impostazione predefinita, i file di origine per i test delle unità locali vengono inseriti in module-name/src/test/. Questa directory esiste già quando crei un nuovo progetto utilizzando Android Studio.
Aggiunta di dipendenze di test
Devi anche configurare le dipendenze di test per il tuo progetto in modo da utilizzare le API standard fornite dal framework di test JUnit.
Per farlo, apri il file build.gradle del modulo dell'app e specifica le seguenti librerie come dipendenze. Utilizza la funzione testImplementation per indicare che si applicano al set di risorse di test locale e non all'applicazione:
dependencies {
// Required -- JUnit 4 framework
testImplementation "junit:junit:$jUnitVersion"
// Optional -- Robolectric environment
testImplementation "androidx.test:core:$androidXTestVersion"
// Optional -- Mockito framework
testImplementation "org.mockito:mockito-core:$mockitoVersion"
// Optional -- mockito-kotlin
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
// Optional -- Mockk framework
testImplementation "io.mockk:mockk:$mockkVersion"
}
Crea una classe di test delle unità locali
Scrivi la classe di test delle unità locali come classe di test JUnit 4.
Per farlo, crea una classe che contenga uno o più metodi di test, di solito in module-name/src/test/. Un metodo di test inizia con l'annotazione @Test e contiene il codice per esercitare e verificare un singolo aspetto del componente che vuoi testare.
L'esempio seguente mostra come implementare una classe di test delle unità locale. Il metodo di test emailValidator_correctEmailSimple_returnsTrue() tenta di verificare isValidEmail(),che è un metodo all'interno dell'app. La funzione di test restituirà true se anche isValidEmail() restituisce true.
Kotlin
import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test class EmailValidatorTest { @Test fun emailValidator_CorrectEmailSimple_ReturnsTrue() { assertTrue(EmailValidator.isValidEmail("name@email.com")) } }
Java
import org.junit.Test; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; class EmailValidatorTest { @Test public void emailValidator_CorrectEmailSimple_ReturnsTrue() { assertTrue(EmailValidator.isValidEmail("name@email.com")); } }
Devi creare test leggibili che valutino se i componenti della tua app restituiscono i risultati previsti. Ti consigliamo di utilizzare una libreria di asserzioni come junit.Assert, Hamcrest o Truth. Lo snippet riportato sopra è un esempio di come utilizzare junit.Assert.
Libreria Android simulabile
Quando esegui test delle unità locali, il plug-in Android per Gradle include una libreria che contiene tutte le API del framework Android, corrette per la versione utilizzata nel progetto. La libreria contiene tutti i metodi e le classi pubblici di queste API, ma il codice all'interno dei metodi è stato rimosso. Se viene eseguito l'accesso a uno dei metodi, il test genera un'eccezione.
In questo modo è possibile creare test locali quando si fa riferimento a classi nel framework Android, come Context. Ancora più importante, ti consente di utilizzare un framework di simulazione con le classi Android.
Simulazione delle dipendenze Android
Un problema tipico è scoprire che una classe utilizza una risorsa stringa. Puoi ottenere risorse stringa chiamando il metodo getString() nella classe Context. Tuttavia, un test locale non può utilizzare Context o i suoi metodi, in quanto appartengono al framework Android. Idealmente, la chiamata a getString() dovrebbe essere spostata fuori dalla classe, ma non è sempre pratico. La soluzione è creare un mock o uno stub di Context che restituisca sempre lo stesso valore quando viene richiamato il suo metodo getString().
Con la libreria Android simulabile e i framework di simulazione come Mockito o MockK, puoi programmare il comportamento delle simulazioni delle classi Android nei test delle unità.
Per aggiungere un oggetto fittizio al test delle unità locali utilizzando Mockito, segui questo modello di programmazione:
- Includi la dipendenza della libreria Mockito nel file
build.gradle, come descritto in Configura l'ambiente di test. - All'inizio della definizione della classe di test delle unità, aggiungi l'annotazione
@RunWith(MockitoJUnitRunner.class). Questa annotazione indica al test runner di Mockito di verificare che l'utilizzo del framework sia corretto e semplifica l'inizializzazione degli oggetti fittizi. - Per creare un oggetto fittizio per una dipendenza Android, aggiungi l'annotazione
@Mockprima della dichiarazione del campo. - Per simulare il comportamento della dipendenza, puoi specificare una condizione e un valore restituito quando la condizione viene soddisfatta utilizzando i metodi
when()ethenReturn().
L'esempio seguente mostra come creare un test delle unità che utilizza un oggetto Context fittizio in Kotlin creato con Mockito-Kotlin.
import android.content.Context
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
private const val FAKE_STRING = "HELLO WORLD"
@RunWith(MockitoJUnitRunner::class)
class MockedContextTest {
@Mock
private lateinit var mockContext: Context
@Test
fun readStringFromContext_LocalizedString() {
// Given a mocked Context injected into the object under test...
val mockContext = mock<Context> {
on { getString(R.string.name_label) } doReturn FAKE_STRING
}
val myObjectUnderTest = ClassUnderTest(mockContext)
// ...when the string is returned from the object under test...
val result: String = myObjectUnderTest.getName()
// ...then the result should be the expected one.
assertEquals(result, FAKE_STRING)
}
}
Per scoprire di più sull'utilizzo del framework Mockito, consulta il riferimento API Mockito e la classe SharedPreferencesHelperTest nel codice campione. Prova anche il codelab per i test di Android.
Errore: "Method … not mocked"
La libreria Android simulabile genera un'eccezione se provi ad accedere a uno dei suoi metodi con il messaggio Error: "Method ... not mocked.
Se le eccezioni generate sono problematiche per i test, puoi modificare il comportamento in modo che i metodi restituiscano null o zero, a seconda del tipo restituito. Per farlo, aggiungi la seguente configurazione nel file build.gradle di primo livello del progetto in Groovy:
android {
...
testOptions {
unitTests.returnDefaultValues = true
}