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
private
etfinal
. - Les méthodes
public
à disposition sont toutesstatic
et 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
final
etprivate
lè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).