Créer des tests unitaires locaux

Un test local s'exécute directement sur votre propre poste de travail, plutôt que sur un appareil ou un émulateur Android. Par conséquent, il utilise votre machine virtuelle Java (JVM) locale plutôt qu'un appareil Android pour exécuter les tests. Les tests en local vous permettent d'évaluer plus rapidement la logique de votre application. Toutefois, l'impossibilité d'interagir avec le framework Android crée une limitation dans les types de tests que vous pouvez exécuter.

Un test unitaire permet de vérifier le comportement d'une petite partie de code, l'unité testée. Pour ce faire, il exécute ce code et vérifie le résultat.

Les tests unitaires sont généralement simples, mais leur configuration peut poser problème lorsque l'unité testée n'est pas conçue dans un souci de testabilité:

  • Le code que vous souhaitez vérifier doit être accessible depuis un test. Par exemple, vous ne pouvez pas tester une méthode privée directement. À la place, vous testez la classe à l'aide de ses API publiques.
  • Pour exécuter des tests unitaires de manière isolée, les dépendances de l'unité testée doivent être remplacées par des composants que vous contrôlez, tels que des faux ou d'autres doubles de test. Cela est particulièrement problématique si votre code dépend du framework Android.

Pour en savoir plus sur les stratégies courantes de tests unitaires sous Android, consultez la section Éléments à tester.

Emplacement des tests locaux

Par défaut, les fichiers sources des tests unitaires locaux sont placés dans module-name/src/test/. Ce répertoire existe déjà lorsque vous créez un projet à l'aide d'Android Studio.

Ajouter des dépendances de test

Vous devez également configurer les dépendances de test pour votre projet afin d'utiliser les API standards fournies par le framework de test JUnit.

Pour ce faire, ouvrez le fichier build.gradle du module de votre application et spécifiez les bibliothèques suivantes en tant que dépendances. Utilisez la fonction testImplementation pour indiquer qu'elles s'appliquent à l'ensemble de sources de test local, et non à l'application:

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"
}

Créer une classe de test unitaire local

Vous écrivez votre classe de test unitaire local en tant que classe de test JUnit 4.

Pour ce faire, créez une classe contenant une ou plusieurs méthodes de test, généralement dans module-name/src/test/. Une méthode de test commence par l'annotation @Test et contient le code pour tester et vérifier un seul aspect du composant que vous souhaitez tester.

L'exemple suivant montre comment implémenter une classe de test unitaire local. La méthode de test emailValidator_correctEmailSimple_returnsTrue() tente de vérifier isValidEmail(),qui est une méthode intégrée à l'application. La fonction de test renvoie la valeur "true" si isValidEmail() renvoie également "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"));
  }
}

Vous devez créer des tests lisibles qui évaluent si les composants de votre application renvoient les résultats attendus. Nous vous recommandons d'utiliser une bibliothèque d'assertions telle que junit.Assert, Hamcrest ou Truth. L'extrait ci-dessus montre comment utiliser junit.Assert.

Bibliothèque Android fictive

Lorsque vous exécutez des tests unitaires locaux, le plug-in Android Gradle inclut une bibliothèque contenant toutes les API du framework Android, avec la version utilisée dans votre projet. La bibliothèque contient toutes les méthodes et classes publiques de ces API, mais le code qu'elles contiennent a été supprimé. Si l'accès à l'une des méthodes est effectué, le test génère une exception.

Cela permet de créer des tests locaux lorsque vous faites référence à des classes dans le framework Android, telles que Context. Plus important encore, il vous permet d'utiliser un framework de simulation avec des classes Android.

Simulation des dépendances Android

Un problème typique consiste à constater qu'une classe utilise une ressource de chaîne. Vous pouvez obtenir des ressources de chaîne en appelant la méthode getString() dans la classe Context. Toutefois, un test local ne peut pas utiliser Context ni aucune de ses méthodes, car elles appartiennent au framework Android. Idéalement, l'appel à getString() serait retiré de la classe, mais ce n'est pas toujours pratique. La solution consiste à créer une simulation ou un bouchon de Context qui renvoie toujours la même valeur lorsque sa méthode getString() est appelée.

Avec la bibliothèque Android de simulation et les frameworks de simulation tels que Mockito ou MockK, vous pouvez programmer le comportement des simulations des classes Android dans vos tests unitaires.

Pour ajouter un objet fictif à votre test unitaire local à l'aide de Mockito, suivez ce modèle de programmation:

  1. Incluez la dépendance de la bibliothèque Mockito dans votre fichier build.gradle, comme décrit dans la section Configurer votre environnement de test.
  2. Au début de la définition de votre classe de test unitaire, ajoutez l'annotation @RunWith(MockitoJUnitRunner.class). Cette annotation indique au lanceur de test Mockito de vérifier que votre utilisation du framework est correcte et simplifie l'initialisation des objets fictifs.
  3. Pour créer un objet fictif pour une dépendance Android, ajoutez l'annotation @Mock avant la déclaration de champ.
  4. Pour bloquer le comportement de la dépendance, vous pouvez spécifier une condition et renvoyer une valeur lorsque la condition est remplie à l'aide des méthodes when() et thenReturn().

L'exemple suivant montre comment créer un test unitaire utilisant un objet Context fictif en Kotlin créé avec 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)
  }
}

Pour en savoir plus sur l'utilisation du framework Mockito, consultez la documentation de référence de l'API Mockito et la classe SharedPreferencesHelperTest dans l'exemple de code. Essayez également l'atelier de programmation sur les tests Android.

Erreur: "Méthode non simulée"

La bibliothèque Mockable Android génère une exception si vous essayez d'accéder à l'une de ses méthodes avec le message Error: "Method ... not mocked.

Si les exceptions générées posent problème pour vos tests, vous pouvez modifier le comportement afin que les méthodes renvoient la valeur "null" ou "zéro", selon le type renvoyé. Pour ce faire, ajoutez la configuration suivante dans le fichier build.gradle de premier niveau de votre projet en Groovy:

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