lundi 18 novembre 2013

Test unitaires pur métier - Architecture Hexagonale

TLDR; Une forme de duplication subtile, que l'on rencontre souvent et qui a pour conséquence des tests unitaires trop techniques qui sont incapables de documenter les règles métier.  Je montrerai une technique pour séparer logique métier d'infrastructure et par conséquent réduire la compléxité du code, augmenter sa valeur pour le métier et utiliser les tests unitaires comme documentation des règles métier. J'ai créé un exemple plus complet pour s'entrainer à ce remaniement.

Le code que l'on va voir est une simplification d'un cas assez classique et tiré d'un projet ou je développais récemment.

Nous avons AccountCreationController (controleur classique mvc) ou l'on traite une requête de création de compte. La règle métier dit que les résultats possibles sont

  • "success" -> on en informe l'utilisateur et on le redirige vers une page où il peut naviguer en tant que connecté
  • "pending" -> la création automatique a échoué pour l'une des raisons suivantes
    • email déjà utilisé
    • siret "restreint"
    • erreur de traitement
Ci-dessous controleur et service. Ne vous attardez pas sur les détails, regardez  surtout la similitude entre le if-else et le try catch dans les deux classes. Bien que complèxe, cela ne ressemble pas à de la duplication, mais ça en est.



Les problèmes
  1. La règle métier se trouve éclatée entre deux classes dans des couches différentes (beurk ceci est une appli 3 couches). Il est donc peu réutilisable, peu clair pour les développeurs.
  2. Les tests unitaires n'arrivent pas à capter et documenter cette règle. Nous ne pouvons pas montrer nos tests au métier ou aux testeurs pour feedback. Nous pouvons pas les joindre à la documentation de l'application.
  3. Compléxité cyclomatique accrue due à la duplication des if-else et des try-catch.
Voici les tests (en vue réduit au noms des méthodes de test Junit), regardez comment ils parlent de true/false/throws/exception. Ainsi ils ne peuvent pas être utilisés pour communiquer avec le métier, ni en tant que documentation de l'application



La meilleure solution que je connaisse à ce problème est de passer un objet callback du controleur au service, grâce à quoi la logique conditionnel du controlleur a totalement disparu! 











Conclusion

Désormais on peut créer des tests unitaires qui captent le métier aussi bien, voire mieux qu'un test haut niveau. Ceci n'était pas possible avant. Comme quoi le code dicte la qualité de nos tests. Au fil du temps j'ai appris à chercher le mauvais design là où les tests me plaisent pas.

Ceci est un exemple de OnceAndOnlyOnce, de Model-View-Presenter et plus particulièrement d'architecture héxagonale côté utilisateur (et aussi ). Cette architecture nous dit de séparer la logique métier de l'infrastructure que l'on utilise pour servir des fonctionnalités. Cela me semble essentiel, pour plein de raisons, et en particulier pour créer une application qui sert au mieux des besoins métier qui évoluent

N'hésitez pas à commenter et prendre le code dans sa version plus complète pour vous entrainer!