GLO-4002 - Site du cours 2023

Résinage : tests de caractérisation

Dans votre carrière, il arrivera certainement un moment où vous devrez modifier ou réusiner un bout de code qui n'a pas été développé selon les bonnes pratiques de développement. En tant que professionnel du développement logiciel, vous tiendrez d'abord à couvrir le bout de code en question de tests unitaires afin de vous assurer de ne rien briser. Au moment où vous vous apprêtez à écrire votre premier test, vous remarquez que vous n'arriver même pas à comprendre le bout de code en question. Comment écrire un test qui valide le comportement, si nous n'avons aucune idée du comportement que le code devrait avoir ?

Comme le nom l'indique, les tests de caractérisation serviront exactement à cet effet. Il s'agit de tests qui ne valident pas un comportement, mais bien qui cherchent à identifier/caractériser le comportement d'un composant donné.

Ces derniers peuvent être écrits à plusieurs niveaux (petit, moyen, grands) afin de caractériser une méthode, une classe et/ou une application. Bien entendu, plus l'étendue sera petite, plus ces tests se révèleront précis et pertinents à la construction de votre compréhension du code en question.

La manière d'écrire des tests de caractérisation est très proche de celle d'écrire des tests unitaires de façon "boîte noire". Toutefois, ici l'intérêt ne sera pas de rendre vos tests unitaires agnostiques aux changements d'implémentation, mais plutôt d'explorer un composant donc le fonctionnement vous est inconnu/incompréhensible.

Ainsi, l'écriture de tests de caractérisation reviendra à dresser un portrait en passant différents entrants au composant sous tests et d'y observer les sortants. Comme illustré dans l'exemple suivant :

public class CalculatorTest {

  @Test
  public void testAdd2And3() {
    Calculator calculator = new Calculator();

    int result = calculator.add(2, 3);

    Assertions.assertEquals(5, result);
  }
}

Bien entendu, il sera probablement difficile de savoir d'avance le résultat attendu ce qui implique que vous devrez commencer par mettre des valeurs fictives ou estimées du résultat pour ensuite modifier ces dernières après une première exécution du test.

D'autre part, les tests paramétriques seront plus souvent qu'autrement un outil essentiel à l'écriture de tests de caractérisation.

En dernier lieu, un outil indispensable aux tests de caractérisation sera l'analyse du code coverage. En effet, nous pourrons suivre la trace en fonction des intrants pour s'assurer de bien comprendre la logique du composant sous-tests tout en considérant les possibles effets de bord. Le code coverage nous indiquera aussi à quel moment nous aurons terminé notre travail d'exploration en évitant de laisser des parties de code non-testées.

Exercices

Pour cet exercice, utilisez le dépôt github officiel-refactoring-characterisation.

  1. En partant des classes CharacterizeMe et MyStruct, écrivez une suite de tests de caractérisation pour la méthode d() de la classe CharacterizeMe.
  2. Résumé le comportement de la méthode en quelques phrases.
  3. Procédé au réusinage des classes CharacterizeMe et MyStruct.
    • Votre réusinage devra tout d'abord être composé de réusinage non-destructif.
    • Pour tout réusinage destructif, assurez-vous de respecter le TDD.

Sommaire

  • Les tests de caractérisation nous permettent d'identifier et d'apprendre comment un composant fonctionne.
  • Les tests de caractérisation sont de nature exploratoire.
  • Les tests de caractérisation sont nécessaires lorsque nous ne comprenons pas comment un composant donné fonctionne et qu'il n'existe aucune autre source d'information (ex : suite de tests unitaires).
  • On ne doit jamais modifier du code qui n'est pas testé et que nous ne sommes pas certain de comprendre.

Solution

Une solution potentielle est fournie dans la branche solution du même dépôt github. Prenez soin de lire le README.md qui explique la solution.