TDD et productivité

Quand on est en phase de développement et que l’on pratique le TDD, il est nécessaire de connaître certaines méthodes pour éviter la répétition, une fatigue des doigts évidente et ainsi améliorer son rendement.

Nous essayerons dans cet article de montrer l’ergonomie d’Eclipse et l’utilisation du clavier pour rendre plus courte et plus agréable une session TDD. Nous présenterons quelques plugins Eclipse et aussi un outil souvent négligé que sont les templates de codes.

Un pattern sera introduit pour faciliter l’utilisation des objet métiers et POJO dans les tests.

Enfin, nous présenterons le concept important du Continuous Unit Testing appliqué à l’IDE et indiquerons et comparerons les plugins Eclipse pour le mettre en œuvre.

TDD et Eclipse

Raccourcis claviers utiles

La programmation sur ordinateur est née avant l’outil qu’est la souris pour PC. Tout développeur sait que privilégier le clavier à la souris aide à la concentration et apporte un gain de temps énorme.

Dans cet article, le but n’est pas d’avoir une liste exhaustive de raccourcis claviers utiles à la programmation mais plutôt de faire un résumé des raccourcis basiques en TDD. Ainsi, nous omettrons volontairement les raccourcis de refactoring qui pourront faire l’objet d’un autre article, le refactoring étant indissociable du TDD. Pour connaitre tous les raccourcis, voici un lien avec poster donnant une liste complète pour Eclipse. A placarder chez tous vos clients ou équipes de développement!

Raccourcis à connaitre lorsque l’on démarre TDD (ou la programmation…) avec Eclipse.

DELETE LINE

L’intérêt de ce raccourci est qu’il peut être exécuté n’importe où sur une ligne de code pour effacer cette dernière. Plus besoin d’aller en début ou bout de ligne ou d’utiliser la touche Shift avant d’effacer.

  • Crtl + D (PC)
  • Cmd + D (Mac)
INSERT LINE BELOW CURRENT LINE

Plus de touche Shift, End/Start of line… ce raccourci vous permet de n’importe où d’insérer une ligne au dessous avec la correcte indentation.

  • Shift + Enter (PC & Mac)
DUPLICATE

Celui-ci est très utile pour dupliquer une ligne d’assertion ou encore des blocs de code qui sont communs à plusieurs tests.

  • Ctrl + Alt + Up/Down arrow (PC)
  • Alt + Cmd + Up/Down arrow (Mac)
MOVE

Pratique pour ré-organiser rapidement vos lignes ou blocs de code pour plus de lisibilité.

  • Alt + Up/Down arrow (PC)
  • Alt + Cmd + Up/Down arrow (Mac)
QUICK FIX

Ce raccourci est le raccourci à tout faire comme son nom l’indique. Il sera utile en TDD entre autre, pour la génération de variable (nom et type) ou conversion de variable de méthode en champ de classe. Par exemple, quand vous instanciez un objet, commencez toujours par écrire ‘new MonObjet();’ et laissez ce raccourci faire le reste!

  • Ctrl + 1 (PC)
  • A définir sur le Mac
ADD IMPORT

Permet d’ajouter les static imports (transforme Assert.assertEquals en assertEquals, ou Mockito.when en when). Bien pratique pour Junit, Mockito, Hamcrest en vue de rendre les tests lisibles.

  • Ctrl + Shift + M (PC)
  • Shift + Cmd + M (Mac)

A noter que pour gérer les static imports trés courant en TDD, le menu Preferences/Java/Editor/Content Assist/Favorites permet de définir une liste de membres static. Le raccourci Content Assist vous suggérera ensuite cette liste même si l’import est manquant.

Key promoter

Une bonne méthode pour vous inciter à préférer le clavier à la souris est d’utiliser ce que l’on appelle les key promoter. Ce sont des plugins qui afficheront, au moyen d’une petite fenêtre, les raccourcis clavier à utiliser quand vous avez manqué à votre devoir de bon développeur! Voici les informations pour Eclipse et IntelliJ.

Un peu d’ergonomie

Eclipse vous offre une grande liberté quant à la disposition et organisation de vos vues (View) dans différentes perspective.

En TDD, la pensée et l’écriture du code font un va et vient entre la classe et sa classe de test correspondante. Cela peut paraitre un détail mais une façon de faciliter cela est de scinder votre vue éditeur java en deux comme le montre l’image suivante (cliquez pour agrandir).

tdd-split-screen

Pour réaliser cela avec Eclipse, faites un click & drag sur un onglet dans votre éditeur et amener le vers le bas (tout en maintenant le button de souris) jusqu’à ce que la séparation de l’écran apparaisse.

Vous pouvez ainsi visualiser le code et les tests correspondants en même temps. Une façon de s’organiser et par exemple d’avoir vos tests dans l’éditeur du bas et vos classes dans l’éditeur du haut.

Eclipse Outline view

Une vue très utile à l’organisation et visualisation des tests est la Outline View. Elle permet de voir rapidement tous les noms des méthodes de test et agit ainsi comme documentation pour une classe. Mais surtout elle permet, en utilisant un drag & drop directement dans la vue, d’ordonner et de grouper logiquement les méthodes de test. Attention pour déplacer les méthodes de votre classe en utilisant la Outline View, le tri alphabétique doit être désactivé pour cette vue (petit bouton AZ dans le menu de la vue).

Ci-dessous un exemple de ré-organisation de méthodes

Templates de code Java

Les templates de code sont une description structurée de patterns de code qui sont récurrents au cours de votre programmation. Eclipse vient par défaut avec une série de templates courants très utiles (Preference > Java > Editor > Templates), mais vous donne surtout les moyens de créer les vôtres avec beaucoup de flexibilité grâce aux variables. Notez que les templates de code peuvent être importés et exportés au format xml très facilement avec Eclipse. Ainsi il est facile de les partager au sein d’un groupe de développement.

Lors de la création d’un template vous devrez choisir un nom et une description. Le nom de votre template se voudra être court et mémorable et peut contenir un chiffre pour faciliter le filtrage.

Par exemple pour générer une méthode de test avec JUnit 4, j’ai appelé mon template t4, et je donne une courte description qui m’aidera lorsque j’aurai de nombreux templates.

Pour ensuite utiliser le template, écrivez le nom du template et utilisez le raccourci du Content Assist (Ctrl + Space sur PC).

Voici quelques exemples de templates:

t4 – Generate Junit4 test method with Mockito imports
${staticImport:importStatic('org.junit.Assert.*')}${staticImport1:importStatic('org.mockito.Mockito.*')}${staticImport2:importStatic('org.mockito.MockitoAnnotations.Mock')} 
@${:newType(org.junit.Test)}
public void should_${testname}() throws Exception {
	${cursor}
}
te – Generate Junit4 expected exception test method
${:import('org.junit.Test')}
@${testType:newType(org.junit.Test)}(expected=${expectionname}.class)
public void should_throw_${testname}_when() throws Exception {
	${cursor}
}
b4 – Generate Junit4 setUp method with Mockito imports
${before:import('org.junit.Before')}${initMocks:importStatic('org.mockito.MockitoAnnotations.*')} 
@${:newType(org.junit.Before)}
public void initBeforeTest() throws Exception {
	initMocks(this);
	${cursor}
}
mk – Generate an instance field with the Mockito (@Mock ) annotation
${:import(org.mockito.Mock)}
@${mockType:newType(org.mockito.Mock)} private ${dependencyType} ${name};
Variables pour templates

La création de templates nécessite de savoir utiliser les variables de templates ce qui reste très simple. Il existe des variables génériques comme ${cursor} ou des variables définies par l’utilisateur qui peuvent donc être réutilisées dans le template. Ces dernières adoptent le format suivant:

  • ${variableUtilisateur:operateurTemplate)}

Exemples:

  • ${mockType:newType(org.mockito.Mock)}
  • ${:newType(org.junit.Before)}

Le nom de la variableUtilisateur peut être laissé vide mais ce nom doit être unique pour chaque template (même vide) pour éviter les conflits.

Les templates trouvent leur sens lorsque la répétition est grande comme c’est le cas en TDD. Cependant, je pense qu’ils sont sous-estimés et encore trop peu utilisés. Faites place à votre imagination pour de nouvelles trouvailles et partagez-les au sein de votre équipe. La lassitude de coder et les tendinites devraient diminuer et la productivité augmentera.

Le plugin MoreUnit

Un plugin qui a commencé à réconcilier TDD et rendement est le plugin MoreUnit qui – après une période assez calme – est à nouveau en développement actif depuis l’été dernier. Une nouvelle version (2.2.0) sort d’ailleurs au moment où vous lisez ces lignes.

Voici les principales fonctionnalités de MoreUnit:

  1. Il décore d’une icône verte discréte toutes vos classes testées. Un moyen rapide de voir ce qui ne l’est pas!
  2. Lors du renommage ou du déplacement d’une classe, MoreUnit se chargera de renommer/déplacer la classe de test correspondante.
  3. Un raccourci (Ctrl+J) vous permet de sauter de votre classe à sa (ses) classe(s) de test, un autre (Ctr+R) de lancer le(s) test(s) à partir de la classe principale.
  4. Ces mêmes raccourcis vous proposent de créer la classe testée en fonction de la classe de test (et vice-versa) si elle n’existe pas, le tout en suivant vos préférences (voir point suivant).
  5. Le comportement plugin est configurable de sorte que vous puissiez utiliser vos propres conventions (quelques exemples: préfixe « should » pour les méthodes de test, méthodes de test nommées selon les méthodes testées, pré-/suffixe « ITest » pour des tests plus orientés intégration, packages de test pré-/suffixés par « test. », tests placés dans un projet séparé, etc…).
  6. Un dernier raccourci (Ctrl+U) vos permet de créer une méthode de test pour une méthode donnée, dans une classe de test existante et correspondant à la classe testée.
  7. Enfin, MoreUnit supporte à la fois JUnit (3&4) et TestNG.

Cela fait maintenant plus de 2 ans que j’utilise moreUnit. Installez-le vous ne le regretterez pas.

Objet métiers et Builder Pattern

Une des tâches les plus ardues lorsque l’on pratique TDD consiste à générer les différentes instances des objets métiers qui seront utilisées pour les tests unitaires.

Imaginons que pour notre test unitaire nous ayons besoin d’une instance de la classe User comme suit

User user = new User();
user.setUsername("bob");
user.setSurname("Martin")
user.setEmail("bob@mail.com")
user.setPhone("+447735463526");

Le code ci-dessus est répétitif et n’aide pas à la lecture.

Une façon d’y remédier, en obtenant un code plus lisible et flexible, est d’utiliser le Builder Pattern. Nous obtenons ainsi des lignes de code simples comme celles-ci

User user = new UserBuilder().username("bob").surname("Martin").email("bob@mail.com")
                          .phone("+447735463526").toUser();

Si l’on veut juste un objet User vide ou l’un avec simplement un email, ce pattern le permet très simplement avec

User userWithEmail = new UserBuilder().email("bob@mail.com").toUser();
User simpleUser = new UserBuilder().toUser();

Voici ci-dessous la classe UserBuilder. Il est important de noter que le champ d’action de cette classe est réduit aux tests seulement et qu’elle n’apparaîtra jamais dans du code de production. Typiquement, en suivant les conventions Maven, cette classe résidera dans src/test/java.

public class UserBuilder {       
	
	private String username;
	private String surname;
	private String email;
	private String phone;

	public UserBuilder username(String username){
		this.username = username;
		return this;
	}        
	
	public UserBuilder surname(String surname){
		this.surname = surname;
		return this;
	}
	
	public UserBuilder email(String email){
		this.email = email;
		return this;
	}

	public UserBuilder phone(String phone){
		this.phone = phone;
		return this;
	}
	
	public User toUser(){
		User user = new User();
		user.setUsername(username);
		user.setSurname(surname);
		user.setEmail(email);
		user.setPhone(phone);
		return user;
	}
}

Il est parfois fastidieux de générer ce type de builder pour tous les objets métiers surtout s’ils sont imposants. Le plugin Eclipse suivant peut aider. Il génère un builder classique pour une classe donnée (la rendant par la même occasion immutable). Moyennant un peu de copier-coller en utilisant la classe produite par le plugin, on peut retrouver le builder présenté ci-dessus sans trop d’effort. Le site update de ce plugin Eclipse est ici.

Continuous unit testing avec Eclipse

Le test unitaire en continu est encore peu utilisé mais est un outil très puissant et indispensable pour le TDD. Il permet de se focaliser uniquement sur l’implémentation et la conception des tests, car votre IDE se chargera de faire tourner les tests unitaires et vous alertera lorsqu’un test est dans le rouge.

Le cycle classique de programmation en TDD est

  1. Écriture du test
  2. Lancer – par raccourci clavier – JUnit pour être dans le rouge
  3. Écriture de l’implémentation
  4. Lancer – par raccourci clavier – JUnit pour être dans le vert
  5. Recommencer le cycle avec un autre test

Avec le test unitaire en continu, intégré dans votre IDE, votre cycle devient

  1. Écriture du test
  2. Écriture de l’implémentation
  3. Recommencer le cycle avec un autre test

On en gagne du temps! Votre IDE (ou plutôt le plugin) se charge du reste… et c’est bien normal. Les développeurs sont là pour la conception après tout!

Les principaux plugins sont:

Plugin gratuit et très simple d’utilisation. Il a cependant des problème de stabilité car la mise à jour et le rafraîchissement dans la JUnit View ne se font pas toujours correctement après un certain temps d’utilisation. Cela pose des problèmes car votre test est dans le vert alors qu’il devrait être dans le rouge! Cependant, c’est idéal pour commencer.

Développé par Kent Beck, il était tombé en désuétude mais a été remis sur le marché en Septembre 2010. Cependant, la licence reste chère à $100 par an! C’est peut être la raison pour laquelle je ne l’ai pas encore essayé. Je suis preneur de retour d’expérience sur ce plugin.

Plugin stable au prix de $20. La notification de test en erreur se fait à l’aide de la Problem View (introduit par JUnit Max) ce qui veut dire que votre plugin ne tournera pas si vous avez une quelconque erreur de workspace. Un peu ennuyeux parfois. La configuration du plugin reste basique. Il n’y a en effet pas moyen de choisir sur quels projets dans votre workspace vous voulez appliquer le test continu. Ce qui veut dire qu’Infinitest, va faire tourner tous les tests de votre workspace. Le filtrage des classes de test par leur nom (pour éviter les tests d’intégration comme *.ITest) est un peu décevant puisque il faut ajouter un fichier de properties dans le projet au lieu d’utiliser un menu Eclipse dans les préférences.

Ayant commencé par utiliser JUnit Flux, j’utilise maintenant Infinitest et c’est celui que je recommanderais pour petit budget – vous pouvez l’essayer gratuitement pour 30 jours – en attendant sur le marché un plugin gratuit plus satisfaisant ou d’avoir le temps pour en développer un !

Conclusion

Aujourd’hui, le TDD est une arme que tout développeur Java (ou tout language qui s’y prête bien) doit avoir à son arsenal. Tout débat autour de cette méthodologie deviendrait inutile si tout le monde la maitrisait. En effet, chaque développeur, pourrait à son gré choisir de l’appliquer ou non en toute connaissance de cause.

Cependant, la maitrise du TDD demande du temps, de la méthode et surtout de la discipline.

Le but de cet article a été de promouvoir les pratiques indispensables. Elles permettent au développeur de se concentrer sur la partie fun du TDD en automatisant et en facilitant certaines tâches sans pour autant qu’il perde le contrôle.

Si la pratique du TDD n’est pas encore grandement répandue, j’espère que cet article aidera à faciliter son apprentissage.

Published by

Publié par Simon Caplette

Simon a rejoint Xebia en 2010 après cinq années passées à Londres où il a travaillé sur diverses plate-formes Spring et J2EE. Depuis trois ans, il applique avec succés les methodes XP et plus particulièrement TDD.

Commentaire

8 réponses pour " TDD et productivité "

  1. Published by , Il y a 12 ans

    Merci pour cet article très détaillé, pas mal de petites astuces que je ne connaissais pas et que je vais pouvoir mettre en pratique très vite :)
    Je pinaille mais l’étape 5, c’est plutôt de refactorer :)
    Le builder m’a déjà servi dans les tests pour indiquer des valeurs par défaut, ca permet également de gagner un peu en phase de développement, même si au final, je ne l’utilise pas très souvent.

  2. Published by , Il y a 12 ans

    Je ne comprends toujours pas pourquoi on en est encore aujourd’hui en 2010 à essayer de promouvoir une méthodologie comme le TDD. Les bénéfices sont tellement évidents …

    Personnellement, l’activité la plus chronophage est la mise en place du contexte de test. Le Builder est une solution élégante. Pourtant il ne m’affranchira pas de créer un graphe d’objets qui définira mon contexte. Je pourrais faire une composition de builders pour y remédier mais je trouve que c’est aller un peu loin …

    En tous cas merci pour l’article et les outils. On ressent une certaine passion/exhaltation pour le TDD et ça fait plaisir.

  3. Published by , Il y a 12 ans

    Très bon article.
    J’utilise ces mêmes raccourcis et MoreUnit qui est vraiment indispensable.

    Concernant le template de méthode de test, j’utilise une version Given-When-Then avec BddMockito.

    Le résultat ressemble à :

    public void shouldBuyBread() throws Exception {
    //given
    given(seller.askForBread()).willReturn(new Bread());

    //when
    Goods goods = shop.buyBread();

    //then
    assertThat(goods, containBread());
    }

    Voir la doc : http://mockito.googlecode.com/svn/tags/1.8.0/javadoc/org/mockito/BDDMockito.html

    Thomas

    Thomas

  4. Published by , Il y a 12 ans

    Bravo, cet article est excellent ! Il souligne à quel point il est important pour un développeur de bien maitriser ses outils.. Cela s’inscrit dans la droite ligne de la notion de software craftmanship.

    A propos du pattern builder, un des gros avantages est de permettre la génération de plusieurs beans ayant de légères différences à partir du même builder.

    UserBuilder builder = new UserBuilder().username(« bob »).surname(« Martin »).phone(« +447735463526 »);

    User user1 = builder.mail(« user1@mail.com »).toUser();
    User user2 = builder.mail(« user2@mail.com »).toUser();
    User user3 = builder.mail(« user3@mail.com »).toUser();

    Et puis, on va dire que jme répète, mais je trouve bien plus lisible l’écriture avec un retour à la ligne à la fin de chaque appel de méthode. Et pour éviter qu’Eclipse ne reformate, il suffit d’ajouter « // » à la fin de chaque ligne.

    Ce qui donne :

    User user = new UserBuilder() //
    .username(« bob ») //
    .surname(« Martin ») //
    .email(« bob@mail.com ») //
    .phone(« +447735463526 ») //
    .toUser();

  5. Published by , Il y a 12 ans

    @Simon – Excellent article, and we both know the true benefits on real implementation

    @Louis – Why do we still need to justify TDD importance in 2010? Well mainly due to company slow to change … and where development & testing are still seen as two separate activities/organization. If you don’t get the buying from your entire organization – it’s difficult to change the « how we do  » thinking.

  6. Published by , Il y a 7 ans

    Un petit passage un peu tardif…
    Je voudrai juste rajouter que l’agent eclipse lombok (https://projectlombok.org/) est très utile pour créer facilement des builders.
    Merci pour ce partage d’expérience.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.