Tests unitaires

Developer tests

-

Atelier

Sylvain MATHIEU - id. FR Delivery - Equasens

Objectifs

  • L'intérêt d'écrire des tests automatisés
  • Anatomie d'un test
  • Améliorer l'adoption via une application concrète

Intérêt(s)

  • State of DevOps Report
  • Documentation vivante
  • Guider la conception
  • Exécution rapide
“Nous ne voulons pas avoir des tests juste pour avoir des tests. Nous voulons des tests pour soutenir notre travail.“

Michael Feathers, Working Effectively With Legacy Code

Anatomie

Un test s'adresse à l'interface publique de notre code : comment notre logiciel fonctionne depuis la perspective d'un utilisateur/consommateur de ce logiciel

---

Un test doit chercher à capturer un comportement attendu du système et non pas une implémentation spécifique

On sépare ainsi la conception de l'interface de notre code de l'implémentation "cachée" derrière cette interface

Note : une interface est la surface d'échange entre deux composants logiciels

3 piliers du test unitaires : lisibilité, maintenabilité et confiance

---

3 étages du test unitaire : Given / When / Then ou Arrange / Act / Assert

---

Les tests ne doivent jamais s'affecter mutuellement (jeu de données et états partagés)

On définit un test unitaire à partir de son point d'entrée et de son point de sortie

Un point d'entrée est ce qui est invoqué/appelé en premier, l'endroit par lequel on entre dans le système

---

Un point de sortie est le résultat ou le comportement que l'on obtient

---

On définit ainsi une unité fonctionnelle

Il existe 3 catégories de points de sortie :
  1. La valeur/exception retournée
  2. L'état
  3. Un collaborateur extérieur

Type 1 - Récupération d'une valeur : les points d'entrée et de sortie sont confondus

Type 2 - Changement d'état du système donc le comportement du système change : il faut alors utiliser une différente fonction pour vérifier que le système se comporte différemment

Type 3 - Appeler un système tiers (logger, envoi d'email, etc...) : qch dont nous n'avons pas le contrôle et dont on pourra donner le change

Nous devrions écrire un test pour un seul point de sortie

---

Dit autrement : se limiter à une préoccupation par test car même si un point d'entrée agit sur *n* points de sortie, ces derniers peuvent échouer différemment

Nommage

Le nom du test devrait exprimer l'exigence/
le comportement désiré du système

---

Le nom du test devrait comprendre les valeurs passées/l'état initial du système à tester et le résultat souhaité du système testé

Nommage

L'unité testée, les conditions de l'appel et l'attendu :

  • estBissextile_annéeNonDivisiblePar4_retourneFaux
  • estBissextile_annéeDivisiblePar4MaisNonPar100_retourneVrai
  • estBissextile_annéeDivisiblePar100MaisNonPar400_retourneFaux
  • estBissextile_annéeDivisiblePar400_retourneVrai

Quand s'arrêter ?

  • Code coverage
  • Écrire un test en premier pour chaque
    ajout ou modification d'un comportement
  • Mutation testing

Quand s'arrêter ?

Mais attention !

À ne pas chercher à tester chaque ligne de code. Viser plutôt la vérification d'un comportement attendu.

---

La maintenance et l'évolutivité en dépendent.

L'expérience dit...

“Il n'y a pas d'astuces pour écrire des tests, il n'y a que des astuces pour écrire du code testable“

Miško Hevery, ex Google et créateur d'Angular

Stratégie de tests

TU : logique et exhaustivité / TI : assemblage des composants / E2E : packaging, démarrage complet et rendu


La pyramide des tests vs le cornet de glace : une démarche pratique favorisant les tests qui vérifient la logique / une unité de comportement (TU), puis le cablâge entre le système et un composant externe (TI), puis le rendu (E2E)

JUnit

  • Notion de suites (v4)
  • Notion de tags (v5)

Autres outils

  • Jest, Javalanche
  • MockServer, GreenMail
  • TestNG, QuickPerf
  • Jasmine
  • Outils intégrés

Kata

FizzBuzz

Application

Créer un composant logiciel capable d'enregistrer un flux. Le flux enregistré devra être nommé comme suit : 'nom-horodatage.extension'

Bonus : varier les modes d'enregistrement, rendre l'enregistrement dés/activable

Conclusion

Un logiciel doit :

  • Fonctionner
  • Être compris
  • Pouvoir être mise à jour

Conclusion

  • Pratiquer, pratiquer, pratiquer
  • La productivité n'augmentera pas immédiatement
  • Le nombre de bugs de logique diminuera drastiquement, de 40 à 90% selon Microsoft-IBM

Ressources

Lien : https://matsyl.github.io/sources-atelier-tu.html