:: Enseignements :: ESIPE :: E4INFO :: 2018-2019 :: Programmation Orientée Objet - Design Patterns ::
[LOGO]

TDD


Ce TD se déroule en autonomie. Les rendus sont à faire sur la plate-forme elearning. Les consignes précises du rendu : Rappel : le travail en autonomie peut inclure des discussions entre vous mais le résultat / le rendu doit être le résultat de VOTRE compréhension.
Le but de ce TD est d'utiliser la méthodologie Test Driven Development (ou TDD) pour résoudre un problème simple la conversion de nombre Romain sous forme de chaîne de caractère en entier et vice-versa.
Comme son nom de suggère, le principe du TDD consiste à écrire dans un premier temps les tests qui vont ensuite servir de base pour valider l'implantation qui sera écrite dans un second temps.

Exercice 1 - Questions

Ne faites pas de copier/coller Wikipedia ! ce qui nous intéresse, c'est votre compréhension et vos mots !

  1. Quel est la différence entre tests unitaires et tests d'intégration ?
  2. D'après vous, dans quel cadre la méthodologie TDD est-elle adaptée ?
  3. Comment vérifier que vos tests unitaires JUnit sont suffisants par rapport au code du programme ?

Exercice 2 - Conversion de nombre Romain vers les entiers

Dans un premier temps, nous allons nous intéresser au cas simple, où l'on cherche à convertir un chiffre romain unique passé sous forme d'une chaîne de caractères en l'entier correspondant.
La table de correspondance des chiffres Romain est la suivante
      chiffre     entier             chiffre     entier
        "I"   ->    1                  "L"    ->   50
        "V"   ->    5                  "C"    ->  100
        "X"   ->    10                 
    

Voici un exemple de test unitaire utilisant JUnit 5.
package fr.umlv.designpattern.tdd;

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

public class RomanNumeralsTest {
  @Test
  public void testToInteger1() {
    assertEquals(1, RomanNumerals.toInteger("I"));
    // a completer
  }
}
    

  1. Créer le fichier de test RomanNumeralsTest et demander à Eclipse (au lieu de le faire à la main) de créer la classe RomanNumerals ainsi que la méthode toInteger.
  2. Compléter la méthode testToInteger1 pour couvrir le test des autres chiffres (on parle de chiffre pas de nombre !).
    Vérifier que votre test ne marche pas :)
  3. Vous pouvez maintenant écrire l'implantation de la méthode RomanNumerals.toInteger.
    Vérifier que le test est maintenant au vert.
  4. On souhaite maintenant gérer les nombres Romain composés de plusieurs lettres. On ne va pour l'instant se restreindre aux nombrex qui se lisent de gauche à droite, comme "VI" pour 6 ou "CII" pour 102, en supposant que les lettres sont toujours écrites dans l'ordre décroissant ('C' est avant 'I' dans "CII").
    Ecrire la méthode de test testToInteger2 qui test une dizaine de nombres romains à plusieurs chiffres (essayer de tester des combinaisons différentes).
    Vérifier que le nouveau test ne passe pas.
  5. Modifier l'implantation de votre méthode RomanNumerals.toInteger pour que les tests soient valides.

Exercice 3 - Conversion d'entiers vers les chiffres Romain

  1. Ecrire le test testToRomanNumerals1 qui vérifie si la méthode RomanNumerals.toRomanNumerals marche bien si on passe en paramètre un nombre correspondant à un chiffre Romain.
    De même que pour l'exercice 1, faire en sorte qu'Eclipse vous génére le squelette de la méthode RomanNumerals.toRomanNumerals.
    Puis vérifier que le test testToRomanNumerals1 ne marche pas.
  2. Ajouter le test testToRomanNumerals2 qui vérifie que la méthode RomanNumerals.toRomanNumerals marche avec des nombres (donc pouvant être composés de plusieurs chiffres).
    Vérifier que le test testToRomanNumerals2 ne marche pas.
  3. Implanter la méthode RomanNumerals.toRomanNumerals.
    L'idée est de tester si le nombre est supérieur à 100 et de mettre un 'C' dans un StringBuilder et de continuer jusqu'à ce que le nombre soit inférieur à 100. Et de faire la même chose avec 50, 10, 5 et 1.
    Si vous ne voyez pas comment faire sans copier coller, faite avec, nous verrons plus tard comment améliorer les choses.
    Vérifier que les tests testToRomanNumerals1 et testToRomanNumerals2 sont verts.
  4. En fait, il manque un test dans le cas où les nombres passés en paramètre sont négatifs ou nuls. Que devrait faire l'implantation de RomanNumerals.toRomanNumerals ?
    Ecrire le test correspondant (testToRomanNumerals3) et vérifier que le test ne passe pas.
    Il y a deux façons d'écrire ce test, soit on utilise assertThrows (recommandé), soit on utilise la méthode org.junit.jupiter.api.Assertions.fail() dans un try/catch.
  5. Modifier l'implantation pour que le test passe.

Exercice 4 - Nombre Romain préfix

En fait il est possible de former des nombres romain en mettant une lettre plus petite devant une lettre plus grande dans certains cas. Par exemple, "IV" peut se lire "I" (1) avant "V" (5) et donc avoir pour valeur '4'.
Il y a plusieurs règles possibles mais la plus utilisée est qu'il est uniquement possible d'utiliser "I" devant "V" ou "X", et "X" devant "L" ou "C".

  1. Ajouter les méthodes testToIntegerPrefix et testToRomanNumeralsPrefix qui testent que les implantations reconnaissent bien ces combinaisons.
    Puis vérifier que les tests ne marche pas.
  2. Modifier les implantations de RomanNumerals.toInteger et RomanNumerals.toRomanNumerals et vérifier que les tests sont OK.
    La déclaration suivante devrait vous éviter les copier/coller dans toRomanNumerals
      private static final int[] VALUES =     { 100,   90,  50,   40,  10,   9,    5,    4,   1 };
      private static final String[] DIGRAMS = { "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
          
    et devrait vous permettre de simplifier toInteger avec l'idée qu'il est possible à partir du tableau des di-grams de créer une seule expression régulière (avec autant de groupes que de di-grams) qui va reconnaitre si une partie d'une chaine de caractère est un di-grams.