02 mai 2014

To test or not to test ?

Pendant des années j'ai fait la promotion des tests unitaires, expérimenté tout un tas d'outils (c'est le côté rigolo) sans moi même trop me plier à la pratique. Je pouvais prétexter intervenir sur des couches techniques et/ou d'intégration qui sont d'un compliqué ma pauv' dame.

Bref faites ce que je dis, pas ce que je fais.

J'ai utilisé le TDD une fois lorsque j'ai voulu ré-implémenter fonzie proprement (la version initiale étant au mieux un jouet, ce qui n'a pas empêché de le mettre en production, m'enfin). Il faut reconnaître que ça marche du tonnerre si on arrive à se discipliner.

J'arrive après la bataille et je ne vais donc pas vous faire l'apologie des tests unitaires, d'autre l'ont fait et le font encore bien mieux que moi avec nettement plus de crédibilité. Ce que je constate par contre, c'est que c'est une pratique qui a un impact fort sur les choix d'architecture et de frameworks.

Architecture : une grosses appli Java EE de centaines de milliers de lignes, avec toute la bonne volonté du monde ça met vite un temps fou à builder/tester. Je pourrais vous parler de micro-services (un autre jour ?) mais il est évident qu'avec une suite de test qui prend plusieurs minutes et écroule la machine, le développeur ne va pas lancer les tests régulièrement, ni sans doute avant de committer des modifs mineures (sic!) sauf à faire la police dans l'open-space - bonne ambiance garantie.
Ca veut donc dire qu'il faut savoir cibler les tests à l'essentiels, le 100% de couverture qui teste tous les getter/setter (si si, y'en a) n'apporte vraiment que du surpoids au projet.

Frameworks : Je vous parlais hier de fluent-http, et sans surprise ce framework web permet d'écrire des tests très léger et très rapides. Il sait démarrer sur un port libre aléatoire (ce qui permet aux tests de tourner en parallèle), et ce en quelques centaines de millisecondes.

public class MyResourceTest {

    @BeforeClass
    public static void runWebServer() throws Exception {
        WebServer server = new WebServer(routes -> {
            routes.add(MyResource.class);
        }).startOnRandomPort();
    }


    @Test
    public void should_create_user() {
        given()
           .body("{ \"name\": \"nicolas\" }")
        .when()
           .post("/user")
        .then()
           .assertThat().statusCode(CREATED);
        // TODO test database insert was successful
    }
}


Ce tout petit test me permet de tester mon service REST, il manque juste un peu de mocking pour la partie backend. Il est de niveau API, donc orienté fonctionnel, et me permet de jouer avec l'implémentation.

Jouer c'est bien le mot, car avec des tests aussi rapides, on peut activer infinitest.

Infinitest joue les tests à chaque modification de code (compilation si vous utilisez IntelliJ Idea). On va donc tripatouiller le code par petites touches, Cmd+S et hop le test vérifie qu'on a pas tout cassé et/ou qu'on a enfin réussi à implémenter la fonctionnalité attendue.

On est donc dans une logique de type micro-intégration-continue, très très efficace. Evidemment, si vous n'avez pas des tests rapides, ça n'est pas d'un grand intérêt. C'est donc un ensemble complet de pratique, d'outils, et d'architecture auquel il faut s'attaquer, tous d'un même front.

Pour en arriver là sur des projets non triviaux, il faut aussi savoir découper son appli correctement. Mais je vous parlerais de micro-services une autre fois :)


30 avril 2014

fluent-http, un amis qui vous veut du bien

Le Call-for-Papers du BreizhCamp est une appli custom développées au fil du temps libre l'an dernier et un peu rafraichie cette année. J'ai pour ambition d'en faire une appli "as-a-service" pour toutes les conférence qui veulent bien s'en servir (n'est-ce pas un super business model ?)

Pour arriver à ce résultat, un grand coup de ménage s'impose, et c'est donc l'occasion pour toute équipe de dev de critiquer les choix techniques "historiques" et essayer de nouvelles choses.

J'ai été tenté par Spring Boot, pour finalement me tourner vers fluent-http. Plusieurs raisons à cela :


  1. fluent-http est un framework tout neuf, c'est donc motivant d'y mettre son nez
  2. fluent-http est fait par l'équipe codestory, avec tout plein de Gageot-eries dedans, et donc c'est beaucoup plus sympa de se dire qu'on peut en discuter avec eux autour d'une bière.
  3. fluent-http est incroyablement léger et rapide. Cela permet d'écrire des tests unitaires (voir plus loin) efficaces
  4. fluent-http nécessite java 8, donc pas d'excuse, il est temps de se mettre aux lambdas et pourquoi par de mettre null hors d'état de nuire à grand coups de Optional.
  5. ça nous fera un sujet pour proposer une conférence :)

L'un des points techniques majeur qu'apporte fluent-http c'est la légèreté. Le serveur démarre en vraiment pas longtemps, et s'il ne supporte pas (encore) le hot-reload le cycle stop/restart prend plus de temps à cliquer qu'à attendre le serveur. Pas d'initialisation à la Spring à parser du XML ou à scanner le classpath à la recherche d'annotations, fluent-http est taillé pour démarrer vite avec juste ce qu'il faut. 

Le "juste ce qu'il faut" est aussi amha un point important. A utiliser des frameworks sensés nous simplifier la vie, on finit par se créer des contraintes et perdre un temps fou sur des problèmes d'intégration. 

Exemple concret : sur la version initiale du CfP, écrite en Play/Java+Angular, la sécurité est confiée à SecureSocial. Sur le papier c'est génial, trois fichier de conf et roule ma poule. Dans la réalité, lorsqu'on veut introduire du multi-tenant, utiliser l'email comme clé d'identification des utilisateurs, etc, on galère (surtout quand l'implémentation LinkedIn ne récupère pas l'email). Ajoutez quelques bugs (?) : je m'inscrit avec github, je fait ma cuisine, je tente de me reconnecter avec Google, et là j'ai un nouveau compte. Ballot non ? 

Au final j'ai passé pas mal de temps à comprendre la structure de SecureSocial, puis finalement a en forker un bout (pour le multi-tenant). C'est certes intéressant, mais tout bien pesé, implémenter mon propre login en comprenant les mécanismes OAuth2 ne prend pas nécessairement plus de temps (la preuve).

Donc, faut-il tout recoder sois-même ? Non, soyons réalistes. Faut-il mettre des frameworks pour tout juste parce qu'ils couvrent mon besoin (plus tout un tas de trucs) ? Non. C'est ce en quoi je ne suis pas fan de JHipster. Sur le papier c'est super, mais rien que la structure de code générée par défaut me fait peur. On peut débattre du bienfait d'avoir tous ces outils pré-cablés, qui seront nécessaires à un moment ou à un autre et ne coutent pas plus cher, mais personnellement ça ne me donne pas envie. J'ai par exemple peur de ce que va devenir mon build Grunt que je n'ai pas du tout conçu moi même. En gros, on m'impose des outils dont je ne sais rien, que je n'ai pas demandés, et que je ne maîtrise pas.

fluent-http se contente (sic) de prendre en charge la couche HTTP et l'exposition de resources REST, en ajoutant le support de templates html. Juste ce qu'il me faut, à moi de définir sur mesure ce que je veux ajouter dessus, au cas par cas.

J'ai glissé un mot sur les tests. Et oui, ça sent le Gageot entre chaque ligne de code. Fluent-http est très rapide à démarrer, non pas pour booster vos serveurs de production (quoi que ...) mais surtout pour permettre d'écrire des tests web rapides. 

Je vous en dirais un mot vendredi, il parait que c'est bien de faire un billet de blog par jour :)