Crea test delle unità locali

Un test locale viene eseguito direttamente sulla tua workstation, anziché su un emulatore o un dispositivo Android. Di conseguenza, per eseguire i test viene utilizzata la Java Virtual Machine (JVM) locale, anziché un dispositivo Android. I test locali consentono di valutare più rapidamente la logica dell'app. Tuttavia, l'impossibilità di interagire con il framework Android crea un limite ai tipi di test che puoi eseguire.

Un test di unità verifica il comportamento di una piccola sezione di codice, l'unità sottoposta a test. Per farlo, esegue il codice e controlla il risultato.

I test delle unità di solito sono semplici, ma la loro configurazione può essere problematica quando l'unità in fase di test non è progettata per testarebilità:

  • Il codice che vuoi verificare deve essere accessibile da un test. Ad esempio, non puoi testare direttamente un metodo privato. Dovrai testare la classe utilizzando le API pubbliche.
  • Per eseguire i test delle unità in isolamento, le dipendenze dell'unità nell'ambito dei test devono essere sostituite da componenti controllati da te, come falsi o altri doppi di test. Ciò è particolarmente problematico se il codice dipende dal framework Android.

Per informazioni sulle strategie di test delle unità comuni in Android, consulta Che cosa testare.

Posizione 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 inoltre configurare le dipendenze di test per il 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 origini di test locali 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"
}

Creare una classe di test delle unità locali

Scrivi la tua classe di test delle unità locali come classe di test JUnit 4.

Per farlo, crea una classe contenente uno o più metodi di test, in genere in module-name/src/test/. Un metodo di test inizia con l'annotazione @Test e contiene il codice da testare e verificare un singolo aspetto del componente che vuoi testare.

L'esempio seguente mostra come implementare una classe di test delle unità locali. Il metodo di test emailValidator_correctEmailSimple_returnsTrue()prova a verificare isValidEmail(),che è un metodo all'interno dell'app. La funzione test restituirà true se ancheisValidEmail() 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"));
  }
}

Dovresti creare test leggibili per valutare se i componenti dell'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 fittizia

Quando esegui test delle unità locali, il plug-in Android per Gradle include una libreria contenente tutte le API del framework Android, corretta 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 usare un framework di simulazione con le classi Android.

Dipendenze Android fittizie

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 nessuno dei suoi metodi in quanto appartengono al framework Android. Idealmente, la chiamata a getString() dovrebbe essere spostata dalla classe, ma questo non è sempre pratico. La soluzione è creare una simulazione o uno stub di Context che restituisca sempre lo stesso valore quando viene richiamato il relativo metodo getString().

Con la libreria Android fittizia e i framework di simulazione come Mockito o MockK, puoi programmare il comportamento delle simulazioni delle classi di Android nei test delle unità.

Per aggiungere un oggetto fittizio al test delle unità locali utilizzando Mockito, segui questo modello di programmazione:

  1. Includi la dipendenza della libreria Mockito nel file build.gradle, come descritto in Configurare l'ambiente di test.
  2. All'inizio della definizione della classe di test delle unità, aggiungi l'annotazione @RunWith(MockitoJUnitRunner.class). Questa annotazione indica all'esecutore del test di simulazione di convalidare che l'utilizzo del framework sia corretto e di semplificare l'inizializzazione degli oggetti fittizi.
  3. Per creare un oggetto fittizio per una dipendenza Android, aggiungi l'annotazione @Mock prima della dichiarazione del campo.
  4. Per arrestare il comportamento della dipendenza, puoi specificare una condizione e un valore restituito quando la condizione è soddisfatta utilizzando i metodi when() e thenReturn().

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 saperne di più sull'utilizzo del framework Mockito, consulta il riferimento sull'API Mockito e la classe SharedPreferencesHelperTest nel codice campione. Prova anche il codelab sui test Android.

Errore: "Metodo ... non simulato"

La libreria Android fittizia genera un'eccezione se provi ad accedere a uno qualsiasi dei suoi metodi con il messaggio Error: "Method ... not mocked.

Se le eccezioni generate sono problematiche per i tuoi test, puoi modificare il comportamento in modo che i metodi restituiscano invece null o zero, a seconda del tipo restituito. Per farlo, aggiungi la seguente configurazione al file build.gradle di primo livello del tuo progetto in Groovy:

android {
  ...
  testOptions {
    unitTests.returnDefaultValues = true
  }