TP Qualité de dev.
S. Salva

TP3 Tests Unitaires TDD


1. Objectifs

Savoir développer des tests unitaires
Faire du test de robustesse
Implémenter des mocks
Faire du Test Driven Development (TDD)

2.

L'objectif final est d'implémenter une classe Money gràce au TDD en écrivant ce que l'on veut avec des tests.

Spécification: La classe Money a pour paramètres amount et  curr représentant un montant et une devise respectivement. La devise s’exprime comme une chaîne de trois caractères définie par la norme ISO (ici, seulement EUR (euro), USD ($), CHF (franc suisse), GBP (livre sterling)). Quand on additionne deux Money de même devise, le résultat a pour montant la somme des deux autres montants. Mais si les devises sont différentes alors il faut convertir.

La structure de la classe est la suivante:

class Money {

private float amount ;
private String curr ;


public Money( float amount , String currency );

public float amount ( ) ;

public String currency ( );

public void addM (Money m);

public void add (float namount , String ncurrency );

}

Dans la suite, vous pouvez coder les tests avec assertions classiques ou  par contrats avec Hamcrest (que je vous invite à essayer).

A

En partant de la structure ci-dessus, écrire la classe Money grâce au TDD.

Je vous propose d'y réfléchir de la façon suivante en commencant à écrire vos tests:

  1. commencez par écrire le les tests pour le (les) constructeur, petit à petit (un test après l'autre
  2. idem pour les getter / settter ????
  3. puis passez à la méthode add(). Réfléchissez bien et écrivez les scénarios possibles avec vos tests (Cela forme la spécification de votre classe).
    De même, n'oubliez pas la fixture (@BeforeEach, @BeforeAll, etc.)

Nous nous limiterons aux devises EUR, USD (1 EUR = 1.29 USD).

Ecrivez chaque cas de test, puis chaque méthode de la classe Money  de façon incrémentale (l'une après l'autre, petit à petit, donc en TDD).

En fin de cette partie, stockez la classe Money

3. Simulation de classes, de dépendances -> Mock (Mockito)

Il est fort probable que vous ayez effectué les convertions euro-dollard dans le code avec l'unité de convertion (1 EUR = 1.29 USD). Pour faire mieux, il plus interessant d'utiliser une Classe composée.

Soit donc la Classe Convertion qui prends une méthode Float unit_Convertion (String s) avec s une chaine de convertion "EUR-USD", "USD_EUR" dans notre cas.

Nous ne souhaitons pas coder cette Classe de suite. Il est important d'isoler la classe Money pour ne tester que cette classe et pas une classe Convertion "codée à l'arrache". Nous allons donc mettre en place un stub (simulation) de cette classe Convertion grâce à Mockito

A

Doc Mockito ICI

Ajoutez la librairie Mockito via votre IDE.

Faites l'import: import static org.mockito.Mockito.*;

Voici un exemple de code qui crée un mock et l'utilise ensuite dans les tests. Remarque: on trouve 2 éléments stub et Mock. Stub simule, Mock simule et est vérifié (avec verify dans cet exemple). Voilà la différence.
class Test1 {
@Mock List mockedList;
//List mockedList = mock(List.class);

@BeforeEach
void setUp() throws Exception {
MockitoAnnotations.openMocks(this);

//using mock object
mockedList.add("one");
mockedList.clear();
when(mockedList.get(1)).thenReturn("element");
when(mockedList.get(2))
.thenThrow(new RuntimeException());

}

@AfterEach
void tearDown() throws Exception {
}

@Test
void test() {


//verification
verify(mockedList).add("one");
verify(mockedList).clear();
//verify(mockedList).get(anyInt());
assertEquals( mockedList.get(1), "element");
}
@Test
void exceptionTesting() {
Exception exception = assertThrows(RuntimeException.class, () ->
mockedList.get(2));

}
}

Revenons à la classe Money à tester en isolation. Créez une classe Convertion vide:
public class Convertion {
public double unit_Convertion(String s) {return 0.0;}
}

Ajoutez l'attribut Convertion dans la classe Money.
Modifiez vos tests.
Et modifiez le code de Money. 

Notez qu'en ce moment même, vous faites du test de  non-régression ! Vous testez le fonctionnement du code que vous avez fait via vos tests.

B

Si ce n'est pas fait, ajoutez du comportement à votre Mock. On va indiquer à Mockito quelle valeur on souhaite retourner lorsque la méthode unit_Convertion est invoquée.

un exemple: when(conv.unit_Convertion("EUR-USD")).thenReturn(1.29);

Corrigez vos tests, puis la classe Money.
Sur un de ces cas de test, vérifiez que la méthode unit_convertion du mock n'est appelée qu'une fois.

C

Ajoutez dans le Mock que si vous recevez un argument commençant par " " dans la méthode unit_Convertion alors vous renvoyez l'Exception IllegalArgumentException

D

Faites de même que précedemment avec les méthodes sub et subM qui effectuent la soustraction deux monnaies en commençant par créer les tests.


4. Tests de robustesse

Il semble logique que les montants ne doivent pas être négatifs. Cela fait parti des tests de robustesse. De même, on ne veut pas de devise nulle ou farfelue. Peut-être y avez vous pensé auparavant ?

A

Créez deux nouveaux test cases afin de tester ces points. Combien faut-il de cas de test par classe ?

B

Modifiez le code de la classe Money pour que vos tests passent.


5. Couverture de code

A

Donnez la couverture de code obtenue avec dans les questions 2 et maintenant (fin de 4). Y a t-il une différence?

Complétez le code de Money et /ou les cas de test (en précisant les modifications) pour obtenir une couverture de 100%

En travaillant de cette façon, vous devriez obtenir une classe Money qui correspond à vos attentes et un code de qualité (gestion des cas non prévus, excepptions. C'est à faire tout le temps, même en entreprise quand le temps le permet).