Pour répondre à des problématiques de projet, les “tech” vont concevoir des solutions/réponses et les mettre en place au sein de leur projet. Ces nouvelles pratiques mises en place dans le projet vont être partagées avec les autres et avec les futurs intervenants, notamment par le biais d’une documentation.
Cependant, il est compliqué de s’assurer que chacun respecte ces pratiques ou les applique correctement.
On peut alors étendre l’utilisation des tests unitaires codés pour vérifier que certaines pratiques sont bien mises en place et continuent à être utilisées au fil de l’évolution du projet.
1. Objectifs d’un DTO
Comme vu précédemment, un DTO est une classe qui doit répondre à ces contraintes :
- La seule utilisation de l’objet est de récupérer les données qu’il transporte.
- Ces données doivent être immuables, c’est-à-dire en lecture seule.
- L’objet ne doit pas mettre à disposition des méthodes publiques.
Si le besoin est de transformer les données, il faut utiliser un autre type d’objet, comme unDataTransformer.
Avec les évolutions de PHP, il est possible de répondre à ces contraintes par le biais des éléments suivants :
- Les propriétés doivent utiliser le mot-clef
readonly, ce qui les rend immuables ; elles ne peuvent être modifiées qu’à l’instanciation de l’objet (dans le constructeur). - Les propriétés doivent être publiques, ce qui permet de les récupérer sans avoir à utiliser de méthode.
Une fois ces règles définies, on peut s’assurer de leur mise en place à travers des tests unitaires et les
ReflectionClass de PHP, qui permettent de récupérer des informations sur une classe. C’est grâce à ces informations
que l’on va pouvoir mettre en place des tests qui vérifient que les règles de développement sont bien respectées.
Lien vers la documentation PHP des ReflectionClass.
1.1 Tester les propriétés
Vous allez compléter vos tests unitaires sur le MediaDto afin de vérifier que toutes les propriétés de la classe sont
définies comme readonly et public.
ReflectionClass::getProperties() et à la classe ReflectionProperty…
Ce test devrait alors vous ajouter 4 x 2 assertions (4 propriétés, 2 assertions différentes).
Rajoutez maintenant une propriété privée dans le DTO et une propriété publique, mais pas en lecture seule (il faudra faire quelques ajustements sur la classe pour pouvoir le faire).
Profitez-en pour supprimer vos tests sur le constructeur, qui ne sont plus utiles.
Vérifiez que phpunit remonte deux failures. S’il n’y en a qu’une, vous devriez sûrement utiliser un Data Provider
plutôt qu’une boucle foreach dans votre test…
Faites un revert de vos modifications sur la classe MediaDto afin que vos tests soient à nouveau au vert.
1.2 Tester les méthodes
Vous allez compléter vos tests unitaires sur le MediaDto afin de vérifier que la classe ne met à disposition aucune
méthode définies comme public.
ReflectionClass::getMethods() et à la classe ReflectionMethod…
⚠️ Le constructeur est une méthode définie comme
public, elle doit être autorisée dans ce test.
Lorsque vous êtes dans ce cas de figure, utilisez l’instruction$this->addToAssertionCount(count: 1);afin d’indiquer qu’une assertion a été faite.
Ce test devrait alors vous ajouter une assertion supplémentaire.
Rajoutez maintenant trois méthodes dans votre DTO pour public, protected et private. Vérifiez que phpunit rajoute
trois assertions supplémentaires dont une en échec.
Faites un revert de vos modifications sur la classe MediaDto afin que vos tests soient à nouveau au vert.
1.3. Tester tous les DTOs
Vous avez écrit des tests pour une classe DTO, ce qui signifie qu’il faudrait faire la même chose pour toutes les
classes DTO du projet. Par ailleurs, vous devez vous assurer que chaque nouveau DTO aura des tests unitaires.
Cela semble être une tâche répétitive et fastidieuse, qui a de fortes probabilités d’être oubliée au fil du temps.
Vous allez donc créer un test applicable à l’ensemble des classes DTO du projet, c’est-à-dire toutes les classes
correspondant au pattern *Dto.php et incluses dans le namespaces TroisOlen\PhpunitTp\Model (ou dans le dossier
src/Model/).
Vous devriez alors avoir trois tests pour les méthodes (correspondant au constructeur des trois DTOs existants) et
neuf tests (soit dix-huit assertions) pour les propriétés (trois pour le AnswerDto, deux pour le QuoteDto et
quatre pour le MediaDto).
Une fois ces tests écrits et valides, vous pouvez supprimer la classe de test sur MediaDto, qui a été remplacée par
vos nouveaux tests.
2. Objectifs d’un AbstractStaticFactory
Le projet met à dispositions des classes Factory en se basant sur le design pattern éponyme.
Une “factory” est une classe permettant d’instancier un object d’une classe précise, en fonction de paramètres donnés. L’objectif de cette classe est de centraliser la création d’objects complexes afin de ne pas avoir besoin de dupliquer du code ou à utiliser des constructeurs statiques directement dans la classe concernée.
Ainsi une classe FooFactory permettra d’instancier des objects de la classe Foo.
Les factories peuvent être des services (c’est-à-dire des classes instanciées une seule fois et injectées en tant que dépendances dans d’autres classes) ou des classes “statiques” (c’est-à-dire des classes qui ne sont pas instanciées et dont les méthodes sont appelées directement).
C’est justement ce dernier cas qui est mis en pratique dans le projet à travers la classe abstraite
AbstractStaticFactory qui s’assure que les classes qui en héritent ne puissent pas être instanciées en rendant le
constructeur private (non appelable en dehors de la classe, ou dans les classes filles), final (non surchargeable
dans les classes filles) et levant une exception.
Selon les mêmes concepts que précédemment, vous allez utiliser les tests unitaires pour vérifier que les classes
Factory héritant de la AbstractStaticFactory n’aient aucune méthode public qui ne soit pas static ET un
constructeur private et final.
2.1 Tester les méthodes
Vous allez écrire un test pour la classe MediaTypeEnumFactory qui vérifie les conditions suivantes sur l’ensemble de
ses méthodes :
- Le constructeur est
privateetfinal. - Les méthodes
publicà disposition sont toutesstaticet qu’il y en ait au moins une.
Une fois ces tests prêts, vérifiez le rapport de PHPUnit et rajoutez une méthode public non static, une méthode
protected et une méthode private. Vous devriez alors avoir un test en “FAILED”.
Faites un revert des modifications de la classe MediaTypeEnumFactory pour que vos tests passent en “OK”.
ℹ️ Il ne sera pas possible de tester la surcharge du constructeur ou l’appel au constructeur dans la classe factory puisque les mots-clefs
finaletprivatelèvent une erreur PHP à l’exécution.
2.2 Tester toutes les factories héritant de AbstractStaticFactory
Comme précédemment avec les DTO, les tests que vous venez d’écrire ne s’appliquent qu’à une seule factory or, nous aimerions qu’il y ait que ces tests soient communs à toutes les factories concernées.
Faites en sorte de récupérer toutes les classes *Factory présentent dans le namespace TroisOlen\Phpunit\Factory
(ou dans le dossier src/Factory/) et héritant de la classe AbstractStaticFactory, puis de réaliser les tests
que vous avez définis.
Une fois ces tests écrits et valides, vous pouvez supprimer ceux de la classe MediaTypeEnumFactory. Faites attention
à ne pas supprimer les tests sur la méthode getFromConverter() qui sont toujours utiles et pertinents.
Retour aux concepts de base
Avec la “Réflexion”, vous obtenez des tests plus poussés et pouvant même répondre à des problématiques particulières, voire bloquantes dans certains cas. Il s’agit quand même d’une utilisation avancée de PHP et de PHPUnit et vous allez retrouver quelques concepts de base, à savoir les “doublures” (🇬🇧 doubles).