Contrôler le temps pour faciliter les tests

Bien souvent, il est difficile de tester du code dans lequel l'écoulement du temps a une grande importance :

  • dans la téléphonie (avec des durées de sonnerie, de conversation) avec des statistiques en bout de chaine
  • pour des tests de statistiques liées à des évènements de manière générale
  • pour des purges de fichiers qui s’appuient sur une date de validité
  • passage heure d'été heure d’hiver
  • applications avec des fuseaux horaires différents
  • tests de calcul de temps
  • … et des tas d’autres exemples que vous trouverez aisément dans vos bases de code

Ne serait-il pas pratique de pouvoir disposer du temps tel un magicien et faire que par exemple tous les tests d’une classe se déroulent le 10 janvier 2010 à 10h00 parce que cette date m’arrange et que je suis capable d'écrire des assertions qui seront toujours valables ?

Nous avons donc développé une librairie de temps simulé que nous appelons ici BarreVerteTime.

Cette librairie est très facile d’utilisation. Il suffit de se déclarer en mode temps simulé et de donner la date que l’on désire pour le test. L’inconvénient de notre librairie est qu’elle a un impact dans le code de production : nous ne pouvons plus faire de new Date() ou jouer avec les Calendar (et c’est tant mieux pour cette partie là). A la place, on utilise la librairie pour obtenir la date courante. Ainsi, dans votre code de production, vous aurez des lignes de ce genre :

C’est un prix à payer mais nous trouvons que cela vaut le coup car nous avons pu améliorer considérablement la testabilité de notre code.

Dans vos tests, il suffit de passer en mode temps simulé et de lui donner la date désirée dans le setup puis de revenir en mode normal dans le teardown.

A la longue, tous les tests se ressemblent dans les parties setUp et tearDown, et une grande duplication entre les tests utilisant  BarreVerteTime apparaît. Imaginez donc un instant que le code ci-dessus apparait dans tous vos tests qui utilisent BarreVerteTime ! Dans notre base de code, ce nombre est vraiment important.

Bien sûr, ceci est intolérable : mais il existe un moyen simple pour factoriser ce genre de code. En effet, depuis jUnit 4.7, il est possible de créer des règles (appelées Rules). Une fois la règle créée, le code pour passer un test en temps simulé tient en une ligne :

Comme vous pouvez le voir, la déclaration est très simple : l’annotation @Rule précède la déclaration de l’objet en visibilité public.

Une règle jUnit est très facile à coder. Nous utilisons ici une ExtrernalResource. Il nous suffit d’implémenter les méthodes before et after qui seront appelées respectivement avant et après chaque test de la classe outillée (comme si vous aviez ajouté des annotations @Before et @After à des méthodes de votre test) :

Et voilà ! Désormais, vous avez le contrôle total sur le temps dans votre logiciel. Comme vous avez pu le constater, rien de magique là-dedans finalement. En outre, vous avez appris à utiliser une des dernières fonctionnalités proposées par jUnit et cela vous a permis d'éviter la duplication dans votre code. Moi, j’appelle cela un bon plan, pas vous ?

NB : nous voudrions ouvrir le code de cette librairie, mais il faut que nous voyions cela auprès de notre employeur. Si nous y parvenons, le code sera disponible sur github.