Cours BUT Info

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érance

Les 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 :

Le cycle JUnit 5 d'une méthode de test : préparer le contexte, exercer et vérifier, nettoyer — puis recommencer pour chaque test, à l'identique. C'est le set-up qui rend les tests répétables et fiables.
AnnotationRôle
@BeforeEachle set-up : préparer le contexte (créer les objets…) avant chaque test
@Testle test : exercer le code et vérifier (assertions)
@AfterEachle tear-down : nettoyer après chaque test

Les grands types de tests

Trois étages de tests, du composant isolé aux conditions réelles. Le développeur garantit le bas de la pyramide ; plus on monte, plus l'exécution est lente et coûteuse — et plus elle est rare.
TypePar quiCe qu'il vérifie
Unitairesle développeurqu'un composant (méthode, classe) se comporte comme prévu
D'intégrationl'équipe d'assurance qualitéla mise en commun des composants et leurs interactions
Fonctionnelsle fournisseur avant livraison, puis le clientle comportement dans les conditions d'utilisation
De recette / d'acceptationrecette technique par le fournisseur, recette utilisateur par le clientla conformité aux spécifications — valide le produit (fin de contrat)
De (non) régressionautomatisé, après chaque modificationl'absence de nouveaux bugs (par exemple en intégration continue)
De performanceéquipe technique, en chargeles temps de réponse (tests de charge)
De robustesseéquipe technique, dans le tempsla 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

Consultation seule — téléchargement désactivé

Chargement du document…

Dernière mise à jour le

Sur cette page