• 8 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 02/02/2022

Testez votre base de données

Avant d'implémenter la base de données SQLite dans notre activité TodoListActivity, nous allons tester les différents appels CRUD et vérifier qu'ils fonctionnent correctement. Cela nous permettra également de valider nos interfaces DAO grâce à différents tests. :)

Créez une classe de test

Afin de commencer à tester les tables de notre base de données, nous allons nous concentrer sur la classe ItemDAO et valider la plupart de ses méthodes CRUD. Pour cela, nous créerons une classe appelée ItemDaoTest dans le dossier dédié à nos tests instrumentalisés, androidTest/, afin qu'ils soient exécutés à partir d'un périphérique Android (plutôt que sur la JVM).

Avant cela, nous devons installer une petite librairie issue d'Android, qui nous facilitera la mise en place de nos tests :

Extrait de build.gradle :

dependencies {

   // TESTING

   androidTestImplementation 'androidx.arch.core:core-testing:2.1.0'
}

Parfait ! Passons maintenant à la création d'une classe responsable des tests effectués sur ItemDAO. Pour cela, créez un dossier androidTest dans app/src. Puis ajoutez un dossier java. Enfin, créez le package com.openclassrooms.savemytrip.

Super ! Vous pouvez ajouter la classe ItemDaoTest ci-après.

Classe ItemDaoTest.java :

@RunWith(AndroidJUnit4.class)

public class ItemDaoTest {

   // FOR DATA

   private SaveMyTripDatabase database;

   @Rule

   public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();

   @Before

   public void initDb() throws Exception {

      this.database = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(),

            SaveMyTripDatabase.class)

            .allowMainThreadQueries()

            .build();

   }

   @After

   public void closeDb() throws Exception {

      database.close();

   }

}

Explications : Cette classe de test sera donc instrumentalisée et exécutée grâce à AndroidJUnitRunner via l'annotation  @RunWith(AndroidJUnit4.class)  . Ce lanceur de test s'occupera de charger le package contenant l'ensemble de nos tests, le tout sur un périphérique Android pour les exécuter.

Nous avons ensuite défini une règle grâce à l'annotation @Rule. Pour rappel, une règle nous permet de définir la manière dont les tests seront exécutés. Dans notre cas, nous avons utilisé ici la règle@InstantTaskExecutorRulepermettant de forcer l'exécution de chaque test de manière synchrone (donc sans les déporter dans un thread en background).

Puis, nous avons créé une méthode   initDb()  qui va se charger de créer une instance de notre base de données, pour ensuite la placer dans la variable   database  déclarée en haut de notre classe. Cette méthode sera appelée avant l'exécution de chaque test grâce à l'annotation @Before.

 Tiens, mais c'est bizarre, le builder pour générer notre classe Room est étrange... o_O

Ah ! Vous l'avez vu...  :) En effet, pour faciliter les tests unitaires, Room nous fournit un builder appelé inMemoryDatabaseBuilder. Ce dernier permet de créer une instance de notre base de données directement en mémoire (et non dans un fichier sur un périphérique !). Pratique, non ?

Testez nos appels CRUD

Passons maintenant aux tests. Dans un premier temps, je vous laisse créer (dans votre package de test) une classe utilitaire nous permettant de lancer plus facilement des méthodes retournant des valeurs de type LiveData.

Classe LiveDataTestUtil.java : 

public class LiveDataTestUtil {

   public static <T> T getValue(final LiveData<T> liveData) throws InterruptedException {

      final Object[] data = new Object[1];

      final CountDownLatch latch = new CountDownLatch(1);

      Observer<T> observer = new Observer<T>() {

         @Override

         public void onChanged(@Nullable T o) {

            data[0] = o;

            latch.countDown();

            liveData.removeObserver(this);

         }

      };

      liveData.observeForever(observer);

      latch.await(2, TimeUnit.SECONDS);

      //noinspection unchecked

      return (T) data[0];

   }

}

Explications : Cette classe est fournie par Google pour vous aider à plus facilement créer des tests impliquant le type LiveData, et surtout à bloquer l'exécution du test tant que le résultat n'est pas retourné.

Testons dans un premier temps l'ajout et la récupération d'un nouvel utilisateur dans notre base SQLite.

Extrait de ItemDaoTest : 

@RunWith(AndroidJUnit4.class)

public class ItemDaoTest {

   // DATA SET FOR TEST

   private static long USER_ID = 1;

   private static User USER_DEMO = new User(USER_ID, "Philippe", "https://www.google.fr, ");

   @Test

   public void insertAndGetUser() throws InterruptedException {

      // BEFORE : Adding a new user

      this.database.userDao().createUser(USER_DEMO);

      // TEST

      User user = LiveDataTestUtil.getValue(this.database.userDao().getUser(USER_ID));

      assertTrue(user.getUsername().equals(USER_DEMO.getUsername()) && user.getId() == USER_ID);

   }

}

 Explications : Avant de créer un test, nous déclarons et instancions un jeu de données statique que nous serons susceptibles de réutiliser dans nos différents tests. Ici nous créons simplement un utilisateur de démo. :)

Puis, nous créons un premier test grâce à l'annotation@Test. Celui-ci va, dans un premier temps, insérer un nouvel utilisateur dans notre base de données (grâce à la méthode  createUser  de notre DAO), pour ensuite le récupérer depuis cette même base de données grâce à la méthode  getUser  . 

Une fois que notre objet   User  a bien été récupéré, nous pouvons tester grâce à la méthode   assertTrue  s'il correspond bien à celui que nous avons précédemment enregistré. C'est aussi simple que cela !

Exécutez maintenant le test ! Félicitations, vous venez de réaliser votre premier test sur une base de données !

Exécution du test
Exécution du test

Cependant, pour le moment, nous avons testé uniquement l'ajout et la récupération de données sur la table User, mais pas encore sur la table Item... Allez, je suis sympa, je vous donne les tests finaux pour tester l'ensemble des méthodes CRUD de la table Item.

Extrait de ItemDaoTest :

public class ItemDaoTest {

   private static Item NEW_ITEM_PLACE_TO_VISIT = new Item("Visite cet endroit de rêve !", 0, USER_ID

);

   private static Item NEW_ITEM_IDEA = new Item("On pourrait faire du chien de traîneau ?", 1, USER_ID
);


   private static Item NEW_ITEM_RESTAURANTS = new Item("Ce restaurant à l'air sympa", 2, USER_ID
);

   @Test

   public void getItemsWhenNoItemInserted() throws InterruptedException {

      // TEST

      List<Item> items = LiveDataTestUtil.getValue(this.database.itemDao().getItems(USER_ID));

      assertTrue(items.isEmpty());

   }

   @Test

   public void insertAndGetItems() throws InterruptedException {

      // BEFORE : Adding demo user & demo items

      this.database.userDao().createUser(USER_DEMO);

      this.database.itemDao().insertItem(NEW_ITEM_PLACE_TO_VISIT);

      this.database.itemDao().insertItem(NEW_ITEM_IDEA);

      this.database.itemDao().insertItem(NEW_ITEM_RESTAURANTS);

      // TEST

      List<Item> items = LiveDataTestUtil.getValue(this.database.itemDao().getItems(USER_ID));

      assertTrue(items.size() == 3);

   }

   @Test

   public void insertAndUpdateItem() throws InterruptedException {

      // BEFORE : Adding demo user & demo items. Next, update item added & re-save it

      this.database.userDao().createUser(USER_DEMO);

      this.database.itemDao().insertItem(NEW_ITEM_PLACE_TO_VISIT);

      Item itemAdded = LiveDataTestUtil.getValue(this.database.itemDao().getItems(USER_ID)).get(0);

      itemAdded.setSelected(true);

      this.database.itemDao().updateItem(itemAdded);


      //TEST

      List<Item> items = LiveDataTestUtil.getValue(this.database.itemDao().getItems(USER_ID));

      assertTrue(items.size() == 1 && items.get(0).getSelected());
   }

   @Test

   public void insertAndDeleteItem() throws InterruptedException {

      // BEFORE : Adding demo user & demo item. Next, get the item added & delete it.

      this.database.userDao().createUser(USER_DEMO);


      this.database.itemDao().insertItem(NEW_ITEM_PLACE_TO_VISIT);

      Item itemAdded = LiveDataTestUtil.getValue(this.database.itemDao().getItems(USER_ID)).get(0);

      this.database.itemDao().deleteItem(itemAdded.getId());

      //TEST

      List<Item> items = LiveDataTestUtil.getValue(this.database.itemDao().getItems(USER_ID));

      assertTrue(items.isEmpty());

   }

}

 Explications : Ces tests sont censés être assez facilement compréhensibles et lisibles... :) Je ne vais donc pas les expliquer en détail. Sachez simplement que nous reprenons l'ensemble des méthodes de notre interface   ItemDao  et nous vérifions qu’elles retournent bien les informations qu'elles devraient retourner.

En résumé

  • Il est assez simple de tester notre base de données avec les différents outils mis à notre disposition.

  • Nous devons ouvrir notre base de données avant notre test avec l’annotation  @Before  .

  • Nous devons fermer notre base de données après le test avec l’annotation  @After  .

Notre base de données est prête et fonctionne correctement grâce à nos tests !

Dès la prochaine partie de ce cours, nous allons voir comment intégrer les appels à notre base de données directement depuis notre contrôleur TodoListActivity, de la manière la plus propre possible, grâce à l'Architecture Components...

Exemple de certificat de réussite
Exemple de certificat de réussite