Ch. 4 — Tests
Pourquoi tester, les grands types de tests, JUnit 5 — et les règles pour écrire du code qui se laisse tester : responsabilités nettes, exceptions vérifiées, calculs séparés de l'interface.
Un test, c'est quoi ?
Un test, c'est un cas d'utilisation d'un logiciel (ou d'une partie) avec spécification des résultats attendus : on exécute du code (par exemple une méthode) et on vérifie que le résultat est correct. Et surtout, retenez ceci : les tests sont programmés — c'est du code.
L'exemple le plus simple du cours cache un piège :
assertEquals(0.8, 0.7 + 0.1); // ✗ échoue !
assertEquals(0.8, 0.7 + 0.1, 0.000001); // ✔ avec une toléranceLes double ne sont pas exacts : 0.7 + 0.1 ne vaut pas exactement 0.8. Dans un test de
nombres flottants, on fixe une tolérance.
Une démarche de qualité
- tester diminue le nombre de défauts ;
- ça sert à conserver la qualité : on re-teste après chaque modification ;
- mais ça ne garantit pas l'absence de défauts :
E. W. Dijkstra, The Humble Programmer (1972)
« Tester des programmes peut révéler des bugs très efficacement, mais cela ne permet pas d'en démontrer l'absence. »
L'outillage : JUnit 5
JUnit est la bibliothèque de test Java (la famille xUnit existe pour tous les langages). Trois annotations suffisent à structurer un test :
| Annotation | Rôle |
|---|---|
@BeforeEach | le set-up : préparer le contexte (créer les objets…) avant chaque test |
@Test | le test : exercer le code et vérifier (assertions) |
@AfterEach | le tear-down : nettoyer après chaque test |
Les grands types de tests
| Type | Par qui | Ce qu'il vérifie |
|---|---|---|
| Unitaires | le développeur | qu'un composant (méthode, classe) se comporte comme prévu |
| D'intégration | l'équipe d'assurance qualité | la mise en commun des composants et leurs interactions |
| Fonctionnels | le fournisseur avant livraison, puis le client | le comportement dans les conditions d'utilisation |
| De recette / d'acceptation | recette technique par le fournisseur, recette utilisateur par le client | la conformité aux spécifications — valide le produit (fin de contrat) |
| De (non) régression | automatisé, après chaque modification | l'absence de nouveaux bugs (par exemple en intégration continue) |
| De performance | équipe technique, en charge | les temps de réponse (tests de charge) |
| De robustesse | équipe technique, dans le temps | la stabilité et la fiabilité (fuites mémoire, pics de charge…) |
Les tests développeur — unitaires, intégration, régression — forment ta base de tests : elle doit être automatisée, rapide (retour immédiat) et facile à lancer (« push button »). C'est ton filet de sécurité contre les régressions.
Le coût
Ça coûte cher… de ne pas tester
« Les tests représentent entre 30 à 50 % des coûts de développement » — Comité Français des Tests Logiciels. Et « les meilleures sociétés ne découvrent et ne corrigent que 85 % des défauts ». Comme le résume le cours : « Si vous pensez que tester en premier est coûteux, essayez donc de débugger plus tard. »
Développer pour tester
Pour tester un programme, il doit être prévu pour ça. Un test aime les unités (méthodes) avec une fonction bien précise et un périmètre bien délimité.
Si une méthode fait trop de choses : trop d'interactions entre ses activités, un set-up difficile à construire, et en cas d'erreur on ne sait pas d'où elle vient.
Les règles du cours :
-
faciliter la création d'instances des classes à tester ;
-
tester les méthodes publiques est facile ; les protected se testent en plaçant le test dans le même paquetage ; les private sont très difficiles — c'est un signal ;
-
chaque méthode a une responsabilité claire et circonscrite ;
-
chaque méthode a un résultat clair et testable : une valeur de retour calculée, ou un attribut qu'on peut vérifier ;
-
tester la levée d'exception, c'est possible :
@Test public void testCharAt() { String text = "Hello World !"; assertThrows(StringIndexOutOfBoundsException.class, () -> text.charAt(text.length())); } -
tester une interface graphique est difficile → séparer les calculs de l'IHM (c'est le MVC du chapitre 3 !) et exposer des méthodes d'accès aux données affichées/saisies ;
-
ne pas vérifier avec
System.out.println: relu une seule fois, sujet aux fautes d'inattention. Un test, lui, est vérifié tout le temps et automatiquement.
A retenir
- les types de tests : unitaires, intégration, régression, fonctionnels (et recette, performance, robustesse) ;
- écrire le code pour qu'il soit testable : création facile d'instances, limiter les effets de bord, méthodes avec une responsabilité bien délimitée ;
- un test vérifie automatiquement et à chaque exécution — c'est ce qui le rend supérieur à l'affichage de debug.
À suivre : le Test Driven Development (développement dirigé par les tests), plus tard dans le semestre… et en S4 (R4.02).
ch4-tests.pdf
R3.04 · Cours 4 — Fabien Delecroix, base de Nicolas Anquetil · 29 diapositives
Chargement du document…
Dernière mise à jour le
Ch. 3 — Modèle, Vue(s), Contrôleur(s)
Le patron architectural MVC : découpler l'interface de la logique métier — un modèle unique qui notifie, des vues qui affichent, des contrôleurs qui traduisent les actions de l'utilisateur.
Ch. 5 — Principes : modularité
Composer un logiciel en briques : paquetages, les cinq principes de modularité, les cinq règles de Bertrand Meyer et les quatre niveaux de visibilité en Java.