Dernière modification : Dec 08 , 2024

Pratiques et recommandations

F.I.R.ST

  • F - FAST : Les tests doivent s'exécuter rapidement
  • I - Independent : Les tests ne doivent pas créer d'effets de bord entre eux
  • R - Repeatable : Les tests doivent toujours fournir le même resultat à chaque exécution
  • S - Self validating : Les tests doivent valident eux-même le résultat. Aucun log ne doit être consulté pour vérifier son exécution.
  • T - Timely : Les tests doivent être écrits avant le code (TDD)
Livre Cleancode by Bob Martin

Cibler la pertinence des fonctionnalités à tester

Chaque user-story ou feature peut être évaluée par :

  • Le risque (réel sur l'utilisateur ou environmental ou financier)
  • La valeur pour le client (estimée ou indiquée par le client lui-même)

Catégoriser ensuite les features selon la technique de MoscoW

Humm.. Mais j'ai du code Legacy et aucune story

Généralement il est possible de créer des stories ou des tests d'acceptations à partir de :

  • la documentation utilisateur
  • les spécifications
  • un inventaire des écrans si une UI existe

Oui mais j'ai pas le temps, je veux une approche par le code uniquement

L'approche par le code est peut être plus simple à appréhender pour le développeur / testeur mais potentiellement les tests sont plus fragiles.
  • sensibilité au refactoring
  • difficulté de comprendre l'intérêt fonctionnel du test en cas de changement du domaine

Oui mais quand même, je veux le faire ainsi

Pour rendre les tests utiles et limiter le risque de fragilité, il faut identifier les "interfaces" ou API dans le code.

  • les interfaces en Java
  • les clusters de classes (groupes de classes indépendants qui possèdent une racine)
  • l'API Rest
  • les commandes et leurs managers (si le patron de conception Commande est implémenté)
  • les méthodes de services Spring si elles ne sont pas trop granulaires
  • les patrons de conceptions de l'application MVC, MVVM

Approche par l'instrumentation

  • identifier les quick wins au niveau couverture de code avec un outil comme SonarQube (les plus gros morceaux non testés sont ceux avec le plus grand risque de problèmes de maintenance)
  • réaliser un profiling de l'application (JProfiler) pour identifier quel code est réellement utilisé par l'utilisateur dans X ou Y scénarios.

Les tests cela coûte cher; on s'arrête quand ?

  • Quand on apprend à développer, on teste avec 100% de couverture afin de comprendre que 80% sont suffisants
  • On s'arrête de tester, quand les risques (régression, insatisfaction utilisateurs, stabilité du code) sont acceptables et acceptés par le client
  • Il vaut mieux peu de tests mais bien compris et qui couvrent une large part de l'application qu'un test unitaire par méthode dans l'application.

Quel est le moyen d’être le plus efficace quand on met en place des tests unitaires ?

Un bon test unitaire ne sauvera pas d'une mauvaise architecture

Corollaire: les applications les mieux testées ont une bonne architecture

Si l'application le permet, il vaut mieux écrire d'abord des tests d'acceptance que des tests unitaires

Utiliser les tests unitaires pour cibler les composants avec le plus de code ou de complexité cyclomatique

Les tests les plus efficaces sont ceux qui s'expriment avec le moins de code, couvrent le plus l'application et dont le résulat attendu est clair.


Faut-il rendre protected des méthodes private pour pouvoir les tester ?

En règle générale, non.

Il faut cibler une méthode publique qui appelle votre méthode privée.

Cas exceptionnel

La méthode peut être un test unitaire pertinent et que les seuls autres moyens de la test est de passer par un test d'intégration, plus fragile de la méthode publique.


Tester entraine-t-il vers des conceptions d’appli particulières ?

ex : méthodes très découpées pour plus de testabilité ?

Oui

Une application bien testée à une architecture bien conçue (designed).

Faut il forcément utiliser la TDD ?

La TDD garantit que le code minimal et suffisant a été développé pour répondre aux tests écrits en amonts. Il ne garantit pas la qualité du code et son élégance. Par conséquent, un code en TDD n'est pas nécessairement bien conçu.

Conseils

Une application testable possède les abstractions nécessaires pour l'écriture des tests d'acceptations, systèmes, d'intégration et éventuellement unitaires.

Tests nécessaires pour garantir sa maintenabilité et satisfaire les exigences utilisateurs.

Quelles sont les abstractions nécessaires ?

Une application facilement testable possède une API métier, exprimant les comportements attendues ou souhaités par l'utilisateur avec des entrées / sorties claires et documentées.

Découper les méthodes en plus petites méthodes n'aide pas si la description fonctionnelle de la méthode est pas claire.

Il est plus facile de tester la méthode Tax::calculerTax(val) : float

Que les méthodes du style :

printResultat(int taux, double val)


Dans quel cas il est crucial de faire des tests ? (pour tous les controlleurs et services ou bien pour des algos ?)

Dans le cas ou votre logiciel peut causer le décès de quelqu'un ou autre conséquence dramatique.

Les tests sont une excellente documentation (à jour) de l'application.
Les tests vous servent d'abord à comprendre ce que fait l'application et pourquoi elle ne fonctionne plus.

D'autres aspects du cours aborde les méthodologies et la priorisation des tests.

Les tests les plus efficaces vérifient des comportements avec des entrées claires et précises et respectent F.I.R.S.T


Est-ce possible de faire des cas « rapides » pour du code existant ?

Les cas rapides sur une application existante dépendent de son architecture et de ses fonctionnalités.

Les meilleurs tests sont :

  • Les tests d'acceptations
  • Les tests systèmes sur les controlleurs ou les API Rests
  • les tests sur les commandes si l'application est implémentée selon CQS

Existe-t-il des astuces pour bouchonner les tests, pour les rendre réutilisables ?

Quand une classe ou une méthode est testée, il importe d'identifier toutes les dépendances nécessaires à l'exécution du SUT.

Les dépendances existent sous deux formes :

  • les dépendances privées (on les utilise directement sans Mock ou Stub)
  • les dépendances partagées : elles doivent être mockées ou stubbées

Voici quelles astuces pour rendre le code facilement "bouchonnable"

  • Utiliser un framework d'IOC
  • Utiliser l'injection par constructeurs que par champs ( et reflection)
  • Créer une interface au-dessus d'un ou plusieurs composants pour abstraire son utilisation
  • Utiliser des frameworks pour émuler le comportement de vos dépendances partagées (HSQLDB, DBUNIT, FAKESMTP, WIREMOCK etc)

Pratiques et recommandations

Created by Sylvain Leroy


Faut-il utiliser des mocks ?

Quelle est la différence entre un spy, un mock et un stub ?

Documentation et resources

Livres intéressants :

  • [Unit testing](Principles, practices, patterns)

Sites intéressants