• 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours est en vidéo.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Mis à jour le 15/04/2019

Testez votre base de données

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

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éer 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ésandroidTest/, 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
    androidTestCompile "android.arch.core:core-testing:1.1.0"
}

Parfait ! Passons maintenant à la création d'une classe responsable des tests effectués sur ItemDAO.

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@InstantTaskExecutorRule permettant 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 ?

Tester 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, cette fois-ci, 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 ! :D

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 si elles retournent les informations qu'elles devraient retourner.

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