GLO-4002 - Site du cours 2025

Temps estimé: 20 minutes

Série d'exercices sur les tests unitaires

Note: Il s'agit de questions ouvertes visant à vous faire réfléchir et intégrer la matière. Les réponses ne sont volontairement pas données.

Question 1

Expliquez en vos mots pourquoi l’apprentissage du TDD peut demander plusieurs mois avant d'être à l'aise à en faire.

Plusieurs réponses possibles. Il y a plusieurs facteurs, en voici quelques-uns qui peuvent arriver au début :

  • Changer de paradigme
  • Différente façon d'approcher un problème
  • Plus difficile au début de découper un problème en petites étapes
  • Difficulté à savoir par où commencer
  • Difficulté à voir les dépendences (collaborateurs) en amont
  • Eetc

Question 2

La fragilité des tests est un problème important.

  1. Expliquer ce qu'est la fragilité des tests
  2. Expliquer quel est le danger d'avoir des tests fragiles
  1. Plusieurs variations possibles, mais en gros "le test échoue pour une raison autre qu'un changement au comportement qui était testé par ce test"
  2. Plusieurs réponses possibles:
    • Difficile à maintenir, car on doit constament ajuster les tests
    • Réduit la facilité avec laquelle on peut faire du refactoring (au lieu de l'augmenter)
    • Réduit la confiance envers les tests
    • Ces tests deviennent souvent ignorés, au lieu d'être corrigés
    • Etc

Question 3

Quels sont les 3 "parties" d'un test?

  1. Arrange : Décrit le "contexte" dans lequel le comportement ce produit
  2. Act : Executer le comportement
  3. Assert : Valider que le comportement s'est produit comme attendu, avec les bons résultats (assert ou verify)

Répondre "given / when / then" ici n'est pas faux. C'est plus une façon de décrire ces parties du test, ce qui est utile plutôt pour nommer le test que pour décrire l'intérieur, mais ça veut essentiellement dire la même chose.

Question 4

On dit généralement qu’un test unitaire ne devrait tester qu’un seul comportement.

  1. Pourquoi est-ce important?
  2. Cela n’implique pas forcément d’avoir une seule assertion (Assert) par test. Pourquoi (quelle est la nuance) ?
  1. Plusieurs raisons possibles, mais selon moi la plus importante est la maintenabilité des tests. Si un test teste plusieurs comportement, il a plusieurs raisons de changer. Si plusieurs tests testent plusieurs comportements, un changement a de grands "ripple effects".
  2. Il y a une différence entre une assertion logique et une physique (un assertEquals() de junit par exemple). Une assertion logique pourrait être, par exemple, pour valider qu'une liste ne contient qu'un seul élément :
@Test
void givenResetCart_whenAddItem_thenContainsOnlyTheNewItem() {
    cart.addItem(new Item("ID1"));
    cart.reset();
    var newItem = new Item("ID2");
    
    List<Item> items = cart.addItem(newItem);
    
    // 2 assertions physiques
    assertEquals(1, items.size());
    assertEquals(newItem, items.get(0));
    
    // Pourrait être converti en 1 seule, si on en fait une méthode privée
    assertContainsOnly(items, newItem);
    
    // Pourrait aussi etre 1 seule avec un framework comme assertJ
    assertThat(items).containsOnlyOnce(newItem);
}

private void assertContainsOnly(List<Item> items, Item id) { /* .... */ }

Question 5

Expliquez le cycle du TDD

  1. Phase rouge : Écrire le minimum de code nécessaire pour obtenir un test qui échoue (assertion ou compilation)
  2. Phase verte : Écrire le minimum de code nécessaire pour faire passer le test
  3. Phase bleue : Faire un refactoring du code. Rouler continuellement les tests pour s'assurer de n'avoir rien brisé

J'aime bien aussi ajouté la phase 0 : S'arrêté et réfléchir un peu avant de commencer! Qu'est-ce qu'on veut tester? Comment on va le tester?

Question 6

Qu'est-ce qu'un test en boîte noire (blackbox) ?

Un test qui ne dépend pas de l'implémentation. Comparez par exemple :

@Test
void givenEmptyClinic_whenTriagePatient_thenPatientIsNextToSeeDoctor() {
    Clinic clinic = new Clinic();
    
    clinic.triagePatient("name", Symptom.MIGRAINE);
    
    // "Leak" l'implémentation d'une stack/queue.
    assertEquals("name", clinic.getDoctorQueue().pop());
    
    // Mieux, on peut choisir l'implémentation
    assertEquals("name", clinic.callNextPatientForDoctor());
}

Question 7

Quel est l'inverse d'un test en boîte noire? En quoi est-ce un problème?

Solution basée sur la réponse à la question 6

Dans cet exemple, on ne pourrait pas choisir de changer l'implémentation facilement. Si on change la stack/queue pour une map ou un objet, on doit changer le test. La conséquence est que le test est fragile (voir réponses aux questions 2 et 4).

Dans le 2e cas, on peut changer la structure interne sans affecter le test.

Question 8

Quels sont les risques de ne pas faire du TDD? En nommer au moins 2.

Plusieurs réponses possibles, voici quelques exemples :

  • On ne se place pas en position "utilisateur" de sa classe et on risque de faire une classe difficilement utilisable pour le reste du système
  • On ne met pas autant de pression sur le design. On risque de manquer une délégation ou une méthode qui simplifierait le code. Avec un test on s'en rendrait peut-être compte aussi, mais si on l'écrit après il faut tout refactoré...
  • On risque de finir avec un design qui en fait "plus que le client en demande". Avec le TDD, on se force à faire le minimum requis à chaque étape. On a tous une tendance au BDUF (big design up front).

Note: Attention dans la réponse à ce que ça ne soit pas une réponse à "Quels sont les risques de ne pas avoir de tests", c'est 2 choses différentes.