Ce qui suit est tiré de la conférence de David Gageot [ @dgageot / http://javabien.net ] à SoftShake 2010.
Sur une vingtaine de personnes présentes dans la salle, les durées de build vont de quelques minutes à plusieurs heures. David présente brièvement quelques frameworks comme Infinitest et JUnitMax. Ce sont des plugins pour IDE Java qui permettent de lancer les tests unitaires en continu et de manière intelligente, c’est à dire uniquement ceux impactés par le code modifié.
La première idée lorsque l’on cherche à optimiser cette durée d’exécution, c’est de vouloir déléguer le problème. C’est faire tourner les tests sur des serveurs distribués qui permettront d’exécuter les tests en tâches de fond. C’est une mauvaise idée, les serveurs coûtent chers et on peut se retrouver submerger. Il existe des méthodes plus simples pour réduire cette durée.
Le KISS ( Keep It Simple, Stupid ) est également applicable lorsque l’on crée des tests. Chercher à optimises ses tests peut améliorer votre produit : ce qui est simple à tester sera simple à coder et à utiliser. Ce qui est compliqué n’en vaut surement pas la peine.
La manière la plus simple pour accélérer les tests c’est d’acheter une machine plus rapide. Exécuter les tests sur une machine plus rapide peut être un vrai gain de temps, David nous donne l’exemple d’une exécution 15% plus rapide sur la machine la plus rapide par rapport à la plus lente. Il est également possible d’utiliser la nouvelle fonctionnalité de maven de build en parallèle (mvn -T2 clean install / mvn -t4 clean install). Nous avons essayé sur un de nos projets, l’exécution du build est passé de 1m30 à 30 secondes !
Il est également possible de faire en sorte que les tâches maven surefire pour JUnit et TestNG soient exécutés en parallèle. Comme les tests se doivent d’être indépendant et isolés, ce sont de bons candidats à une exécution en parallèle. Faire quand même attention que vous pouvez vous retrouver avec des problèmes de concurrence dans certains cas.
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.5</version> <configuration> <parallel>methods</parallel> <threadCount>4</threadCount> </configuration> </plugin>
Il existe deux façons de paralléliser : méthodes ou classes. Utiliser la méthode de parallélisation par méthode peut se révéler risqué car il est fort probable que tous les tests n’auront pas été designés dans l’optique d’être exécutés en parallèle, la méthode ‘classes’ est un choix plus prudent. Plus d’infos sur le blog de Wakaleo Consulting.
Il y a souvent des tests redondants dans un projets : débusquez les, cela permettra de gagner en exécution et en lisibilité. C’est tellement simple qu’on ne le fait pas ! Ne pas hésiter à supprimer des fonctionnalités et les tests associés si elles ne servent plus rien, le projet y gagnera en simplicité.
Les accès réseaux et disques sont trop lents. Il faut essayer au maximum de s’en passer et privilégier les bases de données en mémoire comme h2 (qui ressemblera plus à mysql que hsqldb). De même pour les accès mails, il est possible d’utiliser dans les tests des serveurs SMTP en mémoire comme ethereal. Si beaucoup de tests accèdent à des fichiers, Spring Resource ou Apache VFS (Virtual File System) sont de bonnes alternatives.
Il est préférable de tester les règles métiers dans les tests unitaires plutôt que dans les test d’intégrations. Il ne faut pas confondre tests d’intégrations et tests unitaires : les premiers sont , bien qu’essentiel, plus longs à tester, ils doivent être utiliser avec parcimonie. Par exemple, pour plusieurs tests qui accèderaient à une base de données peuvent être remplacés par un test qui permet de garantir que l’on peut bien accéder à la base de données et par plusieurs autres tests où l’accès à la base de données aura été bouchonné.
Une méthode lorsque l’on cherche à diminuer la durée d’exécution de tests est de prendre le test d’intégration le plus long et de l’analyser jusqu’à réussir à le découper en un test d’intégration plus petit et plusieurs tests unitaires. Si c’est l’accès à certaines couches qui sont lentes lors de tests d’intégrations, il est recommandé de les bouchonner, les frameworks de mocks ne servent pas que dans le cas de tests unitaires
De même, méfiez vous des tests d’interface, ils prennent beaucoup de temps et souvent, ce qu’ils testent peut être tester unitairement. Selenium est à utiliser avec modération. Méfiez vous vraiment quand vous commencez à tester vos fonctionnalités via Selenium. Et ne dites pas ‘mon utilisateur veut de l’AJAX‘ ‘J’ai besoin de tester la compatibilité des différents navigateurs’.
Chaque complexité a un coût. Et cela se paye à chaque fois que les tests sont exécutés. Si c’est compliqué à tester : danger, la fonctionnalité peut sûrement être faite plus simplement. Il est possible de faire des tests unitaires en Javascript plutôt que tester dans les browsers (ex : QUnit).
David préfère limité AJAX aux réels besoins et d’effectuer au maximum le code server-side.
Et pour finir : simplifier et optimiser votre code. Ce sont des choses qui se font. Le build va être plus rapide et l’application aussi A vous l’effet Kiss Cool
]]>J’ai participé à TDD/ATTD et à la session sur les commentaires dans le code.
Le Test Driven Development est une méthode de programmation basée sur les règles suivantes :
L’Acceptance Test Driven Development (ATDD) est basée sur l’idée de faire des tests d’acceptance (automatisés) avant même de commencer les développements. La bonne mise en pratique se fait en deux temps : le client, les utilisateurs, la MOA écrit les test pendant que les développeurs se chargent de les automatiser. Plusieurs outils existent pour cela comme par exemple Greenpepper, Fit/Fitnesse, Robot Framework … La plupart se base sur des wiki pour permettre aux personnes fonctionnelles d’écrire facilement leurs tests.
Dans une approche basée sur l’ATDD, les spécifications et les implémentations sont dirigées par des exemples concrets. Décrire précisément des cas de tests oblige celui qui spécifie à aller plus loin que les spécifications habituelles. Cette méthode a également l’avantage d’être plus claire pour le développeur.
Peu de personnes font du TDD pur et dur, la plupart de la dizaine de personnes présentes écrivent un bout de code avant de tester. Néanmoins, la personne ayant le plus d’expérience est un adepte du TDD pur et dur depuis de nombreuses années. Les frameworks de mocks permettent de simplifier sensiblement la tâche du développeur. Néanmoins, le TDD est loin d’être une évidence pour tout le monde. Le risque de perdre de vue l’architecture générale du projet, la difficulté de la sensibilisation d’une équipe aux tests, les problèmes de maintenabilité et de refactoring sont quelques uns des exemples soulevés pour montrer les difficultés engendrés par l’application de cette méthode.
Au niveau des tests d’acceptance, seul une seule personne les utilise dans un contexte d’entreprise.
Le gros problème soulevé par les tests d’acceptance c’est la difficulté, lors d’utilisation d’outils type fit/fitness greenpepper est la refactorisation des tests : les tests écrits sont difficilement maintenable dans le temps. L’ATDD permet au développeur d’avoir des spécifications claires et précises, permettant entre autre de réduire les difficultés de communication entre la maitrîse d’oeuvre et d’ouvrage. Un autre point bloquant est que l’ATDD demande à l’équipe fonctionnelle de s’impliquer et de se former à un nouvel outil, ce qui n’est pas simple dans certains contextes.
Un autre aspect négatif est le temps pris par les tests, qu’ils soient d’intégration, d’acceptance … Ils ont tendance à grossir encore et encore, à devenir de moins en moins maintenables jusqu’à devenir contre productif. Plutôt qu’un enchaînement d’action, difficile maintenable dans le temps, il faut se focaliser sur les work flows et sur les règles business. Il faut également essayer de limiter quand cela est possible l’utilisation de l’IHM dans les tests d’acceptance.
Olivier Croisier a pour projet de synthétiser les bonnes pratiques de commentaires. Plusieurs points ont été abordés dans cet atelier.
Au niveau des APIs publics, la javadoc est coûteuse à maintenir mais obligatoire, et en anglais. Lorsqu’il y a du code, il est souvent obsolète donc à utiliser avec précaution.
Au niveau du code non public, les personnes présentes utilisent beaucoup moins la javadoc. On remarque souvent qu’il n’y en a pas, qu’elle est obsolète ou inintéressante. Un point pour commenter facilement le code est l’encapsulation du code dans des méthodes private qui, via leurs noms, sont auto-documentées. On peut donc alors comprendre facilement une méthode principale en lisant la succession de méthodes appelées. A noter qu’il ne suffit pas de découper son code n’importe comment, il est important de ne pas avoir une profondeur d’appels de méthodes de ce type trop importantes (A qui appelle B qui appelle C qui appelle D qui appelle E). Un moyen de découper convenablement le code est d’éviter d’appeler dans la méthode principale des méthodes fonctionnelles et des méthodes techniques. Le code est plus lisible si on découpe du fonctionnel vers le technique.
J’ai utilisé cette méthode pour découper des classes de plusieurs centaines de lignes avec un fonctionnel un peu compliqué et je trouve que le code est beaucoup plus clair, en cas d’évolution ou de recherche d’un bug, on sait directement où aller. Le problème de cette méthode est que parfois, on ne voit pas certains refactoring qui pourrait faire gagner en lisibilité (là encore, il est possible de perdre de vue la ‘big picture’). C’est pour cette raison que certains estiment qu’une méthode n’a d’utilité que lorsqu’elle est réutilisée et que les méthodes de plusieurs dizaines de lignes sont en fait plus claires. Sur ce sujet, lire Clean Code et Effective Java est essentiel.
Un autre point est l’utilisation des commentaires du style /**** GETTERS AND SETTERS *****/ /***** CONSTRUCTORS *****/ . La encore, cette méthode ne fait pas l’unanimité (et je suis loin d’en être la première fan).
Au niveau de l’utilisation français/anglais, c’est assez mitigé. La plupart écrivent leurs noms de variables et de commentaires en anglais, d’autres se permettent quelques écarts en français et pour quelques un le français est la norme.
Au niveau des tâches (//FIXME //XXX //TODO), la plupart des personnes présentes sont d’accord avec le fait que cela n’est utile que si la tâche nous concerne.
En ce qui concerne les warnings sur les classes, l’idée est d’en avoir aucun : l’utilisation des outils comme PMD, FindBugs, CheckStyle est à bien configurer avant utilisation, car la multiplication des warnings noie les vrais warnings dans de la pollution.
Je retiens également que Implementation Patterns de Kent Beck sera une de mes lectures estivales et que l’outil mvn eclipse:eclipse permet de faire bien plus de choses que prévues !
J’ai passé un agréable moment, j’y étais venue surtout par curiosité et je compte bien retourner à la prochaine ! Ça a duré jusqu’à un peu plus de 23 heures pour moi, ce qui fait quand même une bonne soirée. Énormément de digressions, ça part dans tous les sens mais c’est souvent là que c’est le plus intéressant !
Sur twitter : http://twitter.com/KawaCampParis
]]>– http://www.waterfallmanifesto.org : Site parodique sur les anciennes méthodes de développement (mais hélas toujours d’actualités) .
– http://blog.crisp.se : Vue artistique d’une journée de dev avec la méthode Kanban.
– http://www.scala-lang.org/ : Nouveau langage à typage fort, à la fois objet et fonctionnel, qui fonctionne sur la plateforme Java (compilé en bytecode). Formidable language sur le papier, je reste perplexe quant à son adoption par le plus grand nombre.
– http://www.kiad.org/ : Blog généraliste du PDG d’Owlient. Articles sur le Web de très bonne qualité.
– Bases de données dénormalisées : Article très instructif sur la nouvelle génération de bases de données dites « Dénormalisées », utiles pour les applications effectuant un très grand nombre de lectures et peu de modification.
]]>