1. Installation de PHPUnit
PHPUnit est une librairie PHP permettant d’écrire des tests unitaires et fonctionnels et de les exécuter.
1.1 Composer
Il existe diverses façons d’installer PHPUnit, mais la plus simple est de l’installer via composer
. Puisque PHPUnit
ne doit pas être mis à disposition d’un environnement de production, il est recommandé de l’installer en tant que
dépendance de développement (en précisant l’option --dev
).
composer require --dev phpunit/phpunit
# Ou à partir du script mis à disposition
bin/dev/composer require --dev phpunit/phpunit
PHPUnit a été installé avec la version ^10.5
, la dernière disponible à ce jour.
Pour exécuter PHPUnit, il faut utiliser le binaire phpunit
mis à disposition par composer
, dans vendor/bin/
.
vendor/bin/phpunit
# Ou à partir du script mis à disposition
bin/dev/phpunit
PHPUnit est maintenant installé et prêt à être utilisé et vous allez pouvoir commencer à écrire vos premiers tests.
1.2 Configuration du namespace
Puisqu’il s’agit d’un projet composer, vous devez paramétrer vos namespaces dans le fichier composer.json
.
C’est le cas pour le code source de l’application définie dans le dossier src/
:
{
"autoload": {
"psr-4": {
"TroisOlen\\PhpunitTp\\": "src/"
}
}
}
Il faut faire de même pour les tests unitaires, en ajoutant un nouveau namespace :
{
"autoload": {
"psr-4": {
"TroisOlen\\PhpunitTp\\": "src/",
"TroisOlen\\PhpunitTp\\Tests\\": "tests/"
}
}
}
1.3 Commit
N’oubliez pas de commiter vos changements, en l’occurrence l’ajout de PHPUnit comme dépendance de développement dans le
fichier composer.json
et le nouveau namespace pour les tests unitaires.
ℹ️ Vous êtes libre de gérer vos commits comme vous l’entendez, mais gardez quand même de bonnes pratiques et une méthodologie similaire à celle que vous utiliserez sur votre projet transverse.
2. Premières assertions
Les tests sont généralement écrits dans le dossier tests/
à la racine du projet.
Avec PHPUnit, les tests sont écrits dans des classes qui héritent de la classe TestCase
. PHPUnit va alors rechercher
les méthodes de cette classe qui commencent par test
et les exécuter.
2.1 Tester un DTO
Plusieurs DTO sont mis à disposition. Les DTO sont des objets qui représentent des données applicatives et qui sont immuables, c’est-à-dire qu’ils ne peuvent pas être modifiés après leur création. C’est pourquoi on appelle ces objets des “transferts de données” (Data Transfer Object).
Les tests unitaires à propos d’un DTO sont généralement assez simples : on vérifie que les données passées au constructeur sont bien celles qui sont retournées par les accesseurs.
Arborescence des tests
Généralement dans un projet PHP, vous trouverez dans un dossier Unit/
dans le dossier des tests où sont regroupées
toutes les classes définissant les tests unitaires du projet.
Vous pourrez alors trouver également les dossiers Integration/
ou Functional/
dans le dossier tests/
, donc au même
niveau que Unit/
, pour respectivement les tests d’intégration et les tests fonctionnels.
De cette manière, vous organisez la nature de vos tests et vous pouvez les exécuter par groupe selon vos besoins.
Enfin, au sein de ces dossiers, il est recommandé de garder la même arborescence définie dans le dossier src/
pour
faciliter la recherche des tests.
Ainsi, la classe MediaDto
du dossier src/Model/
aura son test unitaire dans le dossier tests/Unit/Model/
par le
biais de la classe MediaDtoTest
.
Bonne pratique
Au fur et à mesure de vos utilisations des librairies de tests, vous vous rendrez compte qu’on peut faire énormément de tests et écrire des centaines de lignes pour tester une méthode qui n’en fait que 10.
Vous aurez alors rapidement des classes de tests avec des milliers de lignes de code, ce qui compliquera la lecture et la maintenance de ces tests.
J’ai pour pratique de créer des classes de TU pour chaque méthode testée, au lieu d’avoir une seule classe TU pour la classe testée. Je trouve que c’est plus facile de s’y retrouver et de savoir exactement ce qui est testé.
Dans le cas de notre projet, j’aurais alors une classe ConstructTest
située dans l’arborescence
tests/Unit/Model/MediaDto/
pour tester le constructeur de la classe MediaDto
.
Faites comme bon vous semble, ce n’est qu’une simple suggestion.
2.1.1 Création de la classe de test
Créez la classe MediaDtoTest
dans le dossier tests/Unit/Model/
et faites-la hériter de la classe TestCase
:
<?php
declare(strict_types=1);
namespace TroisOlen\PhpunitTp\Tests\Unit\Model;
use PHPUnit\Framework\TestCase;
final class MediaDtoTest extends TestCase
{
public function testTrue(): void
{
static::assertTrue(true);
}
}
Cette classe de test contient une méthode à tester avec une assertion qui vérifie que la valeur passée en paramètre est
bien true
.
2.1.2 Exécution du test
Vu que le test écrit teste que true
est bien true
, il devrait passer sans problème.
Exécutez le test avec PHPUnit :
vendor/bin/phpunit tests/
# Ou à partir du script mis à disposition
bin/dev/phpunit tests/
PHPUnit devrait alors vous retourner un rapport d’exécution en indiquant le nombre de tests exécutés, le nombre d’assertions effectuées, le nombre et le pourcentage de réussite, ainsi que le temps d’exécution et le pic d’utilisation de la mémoire.
Vous pouvez obtenir un rapport plus détaillé en ajoutant l’option --testdox
.
Vous pouvez aussi définir le path à partir duquel PHPUnit doit rechercher les tests à exécuter si vous ne voulez
exécuter que les tests d’un composant ou d’une classe : phpunit tests/Unit/Model/MediaDtoTest.php
, par exemple.
2.1.3 Échec du test
Modifiez la méthode de test pour qu’elle vérifie que true
est bien false
:
public function testTrue(): void
{
static::assertTrue(false);
}
Exécutez à nouveau le test et vous devriez obtenir un rapport d’exécution avec un test en échec.
2.1.4 Tester le constructeur
Supprimez votre dummy test et créez une méthode de test pour tester le constructeur de la classe MediaDto
en
utilisant l’assertion static::assertEquals()
.
Dans votre rapport, il devrait y avoir :
- 1 test exécuté (réussite 100%).
- 4 assertions effectuées.
2.2 Tester une classe Factory
L’application met à disposition plusieurs classes pour divers besoins. Parmi ces classes, il y a des Factory
(basées
sur le design pattern éponyme) qui permettent de créer des objets. La classe la plus simple que l’on va tester est
MediaTypeEnumFactory
qui permet de retourner une valeur de l’enum MediaTypeEnum
à partir d’une chaîne de caractères.
Cette méthode est utilisée notamment par le DataProvider
qui génère des MediaDto
à partir des données du fichier
JSON, contenant une vingtaine d’exemples d’énigmes.
- Créez la classe de test et la méthode permettant de tester la méthode
getFromConverter()
de la classeMediaTypeEnumFactory
. - Créez des assertions correspondant aux deux valeurs de l’enum
MediaTypeEnum
:MediaTypeEnum::MOVIE
etMediaTypeEnum::SERIES
. - Exécutez le test et vérifiez que les assertions sont bien effectuées.
Vous devriez avoir au moins 2 tests exécutés (réussite 100%) et 6 assertions effectuées.
Dans le code de la méthode getFromConverter()
, vous pouvez voir que la méthode lève une exception si la valeur passée
en paramètre n’est pas reconnue.
Ajoutez ce qu’il faut avec l’assertion qui convient pour tester le fait qu’une valeur non reconnue lève bien l’exception indiquée.
Pour la suite…
Vous avez vu les assertions de base, mais PHPUnit en propose beaucoup d’autres comme les valeurs booléennes, ou des comparaisons numériques, ou des types de variables, etc.
L’un des avantages des tests unitaires codés est de pouvoir utiliser le même test pour plusieurs cas de figure, autrement dit pour plusieurs jeux de données.