Certains assertEquals sont plus égaux que d'autres

le 14 mars 2011, par Philippe Blayo

Une barre rouge inattendue. Je scrute le message d'échec du test dans le panneau junit :

Je double-clique sur expected: pour obtenir le comparateur de l'IDE. Zut rien ne se passe ! Du coup je scrolle pour voir tout le message d'erreur, mais je ne distingue aucune différence entre résultats attendu et obtenu. Et là je m'interroge : pourquoi je n'obtiens pas le comparateur quand je double-clique :-( ?

Ca ne vous est jamais arrivé ? Dans mon équipe, ça arrive tout le temps.

Eclipse et intelliJ IDEA réagissent de la même manière. Leur comportement dépend de l'exception qu'il reçoivent :

  • avec AssertionError le double-clic ne fait rien
  • avec ComparisonFailure le double-clic ouvre la belle popup du comparateur :

Quand ces exceptions sont-elles levées ?

  • ComparisonFailure est levée par assertEquals(String, String)
  • AssertionError est levée par assertEquals(Object, Object) et assertThat

AssertionError n'est pas spécifique à junit, c'est une java.lang.AssertionError, elle peut provenir du code lui-même plutôt que du test. L'IDE ne l'interprète pas comme le résultat d'un test.

De loin, les messages semblent identiques [1] car assertEquals(Object, Object) affiche des toString(). Voilà pourquoi les variations de comportement du double-clic surprennent. Certains en viennent à ajouter des toString() pour souligner les différences :

assertEquals(attendu.toString(), obtenu.toString());

C'est dangereux car l'égalité n'est pas déterminée par le equals() de Object, ce qui viole un contrat Java. En plus, ça ne marche pas pour assertThat qui lève AssertionError même pour deux String (assertThat("Philippe", equalTo("Blayo")). J'aimerais plutôt que :

  • equals() détermine la réussite ou l'échec du test (rouge/vert)
  • une comparaison entre les toString() enrichisse l'affichage expected/actual des barres rouges, quelque soit la classe des objets comparés

Suivant le contexte, je connais deux manières d'y parvenir :

  • utiliser une lib qui lève déjà une ComparisonFailure comme FEST-assert
  • coder soi-même une assertion qui lève une ComparisonFailure (custom assertion). En laissant de coté la gestion des null, la substance d'une telle assertion serait :
    static void assertLisible(Object expected, Object actual) {
        if (! expected.equals(actual))
            throw new ComparisonFailure("",
                expected.toString(), actual.toString());
    }

[1] ComparisonFailure met entre crochets la différence bl[ay]o / bl[ya]o, là où AssertionError laisse blayo / blyao. Les messages ne sont donc identiques qu'en apparence. Mais ces crochets ne suffisent pas : une popup ne s'ouvre qu'avec une ComparisonFailure.

Charger les sources avec maven

le 18 février 2011, par Philippe Blayo

Quand j'ai besoin d'essayer une lib sans impacter du code existant, je crée rapidement un projet jetable avec mvn : mvn archetype:generate

Le grain de sable et le papillon

le 2 février 2011, par Bruno Thomas

Ou comment nous nous sommes plantés sur une mise en production simple, provocant des réactions complètement disproportionnées de notre client. Ou plutôt un de nos clients. Nous fournissons un service complet intégrant l'hébergement et le développement logiciel. Chez le client, nous avons également un pôle responsable du "run", et un autre, avec le marketing, qui définit les évolutions du logiciel et de la plateforme. Chez nous, nous avons deux équipes aussi, une équipe "dev" et une équipe "système".

Bus de données, réalisation et tests des spikes

le 27 janvier 2011, par Bruno Thomas

Nous avons choisi d'évaluer les solutions suivantes :
  1. ActiveMQ, broker JMS apache
  2. RabbitMQ, broker AMQP en erlang
  3. Qpid, autre boker AMQP apache
Nos spikes doivent être fonctionnels, et pouvoir être testés en charge, avec une architecture assez proche de ce que nous avons en production. Comme le service est un service web, et que les statistiques sont émises dans des servlets, nous choisissons de réaliser une petite servlet qui envoie un objet représentant une statistique sur le bus de données, et un consommateur qui reçoit cet objet.

Bus de données, prototypage et infrastructures disponibles

le 25 janvier 2011, par Bruno Thomas

Une fois identifiée une catégorie de solution (un bus de données) répondant au besoin du client, nous abordons un problème complexe, les solutions seront structurantes pour le logiciel et difficiles à remettre en cause. Nous sommes devant un choix d'architecture. Nous savons que ce choix nécessitera des développements longs, et que nous devons quand même être capable de mettre en production des changements fonctionnels mineurs pendant ce laps de temps. Donc soit nous sommes capable de faire une implémentation progressive de la solution, soit nous faisons une branche logicielle.

Software craftmanship

le 20 janvier 2011, par Bruno Thomas

Je ne sais pas si vous avez suivi le monde des blogs anglophones ces derniers jours, mais un débat fait rage autour du mouvement software craftmanship. Une polémique était latente depuis le lancement de ce mouvement en 2009 sur le bien fondé et la signification de ce manifeste. C'était perceptible dans les discours de certains participants à la QCon 2010 par exemple. L'étincelle est venue de Dan North avec son article Programming is not a craft.

Changement de vocabulaire grâce à Midje pour Clojure

le 17 janvier 2011, par Jean-Philippe Caruana

Un framework de test pour Clojure, midje, apporte un nouvel éclairage sur la façon de penser son logiciel et d'écrire des tests : il s'agit de la notion de faits. Voici un exemple de test :
  (fact "un exemple de test tout simple"
     (numerical-reverser 103) => 301)
Voici ce qu'en dit l'auteur :

Contrôler le temps pour faciliter les tests

le 12 janvier 2011, par Jean-Philippe Caruana

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 ?

Un bus de données pour les statistiques

le 6 janvier 2011, par Bruno Thomas

Nous maintenons une application WEB grand public développée en JEE avec de fortes exigences en terme de volume de statistiques. Récemment, une nouvelle demande de modification d'architecture de la plateforme par notre client, combinée à de nouveaux objectifs de fréquentation nous a poussé à remettre en cause la gestion de ces statistiques. L'application en elle-même devait rester iso-fonctionnelle, il s'agissait en quelque sorte d'un refactoring d'architecture.