Index
Documentation Générale Qualité
Documentation Classes Metier
Documentation Liste Qualité Code Review
Documentation Liste Qualité Guideline Development
Documentation Bugs Defaults
Documentation Tests Unitaires
Documentation i18n
Documentation SQL Indexation
Documentation Timeframe

Tests unitaires

Date

Par

Quoi

Version

31/08/2023

MB

Création

0.0.1

27/09/2023

Nam

Ajout section création d’un nouveau test

0.0.2

27/09/2023

MD

Restructuration

0.0.3

09/10/2023

MD

Ajout section Quoi tester et Nommage des fonctions de test unitaires

0.0.4

13/10/2023

MD

Ajout de précision concernant les annotations @cover

0.0.5

26/10/2023

NC

Vérifications, mise en page et ajout de précisions

0.0.6

27/10/2023

MD

Correction de petites coquilles

0.0.7

Pourquoi faire des tests unitaires        2

Lancement des tests unitaires        2

Lancement en mode CLI (SSH)        2

Erreurs possibles au lancement du script        2

Préparation des données de test        2

Création d’un nouveau test        3

Création de la classe de test        3

Création des fonctions de test unitaires        4

Quoi tester ?        4

Comment tester ?        4

Quelques cas usuels :        5

Nommage des fonctions de test unitaires        5

Tests Unitaires et bugs        6

Je lance mes tests unitaires et je trouve un bug, que faire?        6

Je suis en développement        6

Je suis en phase de test >= Q3        6

Je rédige des tests unitaires pour des méthodes qui sont sur master        6

Pourquoi faire des tests unitaires

Le but des tests unitaires est de valider le bon fonctionnement des méthodes que l’on écrit. Si une méthode est correctement testée, alors on a l’assurance qu’elle fait bien ce que l’on veut et on minimise les erreurs qui pourraient survenir avec des données que l’on a pas imaginé.

De plus, si un jour on change le fonctionnement de cette méthode et qu’on introduit des bugs, on devrait s'apercevoir immédiatement qu’il y a des bugs car les tests unitaires ne vont plus passer, donc on minimise les risques de régression dans le code.

Lancement des tests unitaires

Pour l'intégralité de la documentation, voir le document dans **tests/doc/Unit Tests -******** - **.docx**

Lancement en mode CLI (SSH)

  1. Se connecter à son serveur de test en SSH.
  2. Se placer dans le répertoire de test du projet (ex: /volume2/jaildirectory/devX/project/tests).
  3. Lancer la commande “sh ./run.sh”. Celle-ci affichera une aide avec toutes les options connues.
  4. Lancer les tests voulus grâce au même utilitaire.
  1. Lancer tous les tests avec “sh ./run.sh -a”.
  2. Lancer un jeu (groupe) de test avec “sh ./run.sh -t <groupe>”.
  3. Lancer un seul fichier de test avec “sh ./run.sh <chemin vers le fichier>”
  4. Lancer un seul test avec “sh ./run.sh <chemin vers le fichier> --filter <nom fonction test>”

Erreurs possibles au lancement du script

Si le script ne fonctionne pas il faut commencer par vérifier que les fichiers “tests/script/ini-file-parser.sh” et “tests/run.sh” aient les saut de ligne au format UNIX (line separator LF).

Il faut aussi penser à déployer son fichier de configuration.

Préparation des données de test

Les données de test vont être versionnées. Il faut donc qu’elles soient anonymisées ! On peut en revanche mettre des données publiques comme les coordonnées d’entreprises par exemple. Sinon, pour les consultants ou les utilisateurs par exemple, il faudra changer les noms, les dates, les IDs, etc..

Création d’un nouveau test

Création de la classe de test

Les tests unitaires doivent hériter de la classe PHPUnit\Framework\TestCase\TestCase.

Pour les tests qui ont besoin d’interagir avec la base de données, nous avons créé une classe générique parente TestUnitaireWithDbImport dans tests/unit/src/TestUnitaireWithDbImport.php qui hérite déjà de TestCase. Ce fichier va faire les actions génériques pour la fonction setUpBeforeClass :  require du fichier config.inc.php, instanciation de DbConnect, vidage de la bd Test et import du fichier Hightekers.sql.

Lors de la création de la classe ne pas oublier de rajouter l’annotation @covers tel que recommandé par la documentation de PHPUnit 9 (voir :
2. Annotations — PHPUnit 9.6 Manual)

Pour les classes qui héritent de TestUnitaireWithDbImport, on peut ajouter les actions suivantes dans la fonction setUpBeforeClass avant l’appel de la fonction parente parent::setUpBeforeClass :

Exemple :

public static function setUpBeforeClass(): void {
        
echo "------------------------------------------------\n";
        
echo __CLASS__ . "::setUpBeforeClass()\n";
        
//récupération nomFichierSQL à importer après l'import Hightekers.sql
        
self::$sqlName = __DIR__ . '/' . 'InternationalisationTest.sql';
        
parent::setUpBeforeClass();
}

Exemple :

public static function setUpBeforeClass(): void {
        
echo "------------------------------------------------\n";
        
echo "ClassMetierManagementFacturesTest::setUpBeforeClass()\n";
        
//récupération nomFichierSQL à importer après l'import Hightekers.sql
        
self::$sqlName = __DIR__ . '/' . pathinfo(FILE__, PATHINFO_FILENAME) . '.sql';
        
//Car certain fonction ne sont pas encore porté dans une classMetier
        
self::$fileToInclude = ['model/global.inc.php', 'model/paiements.inc.php'];
        
parent::setUpBeforeClass();
        
// Instanciation de la classe métier
        
self::$classMetierManagementFactures = new ClassMetierManagementFactures(self::$dbConnect);
}

Modification du fichier de config

Les classes sont regroupées par “section”, par exemple “Internationalisation” dans un fichier de configuration : “test/conf/app.conf”.

Une section peut faire référence à plusieurs tests unitaires s’ils sont de la même catégorie, par exemple “Internationalisation” et “InternationalisationDyn”.

Un test peut apparaître dans plusieurs catégories : Il est possible de tester un set de fonctionnalités qui sont en rapport les unes avec les autres.

Un nouveau test unitaire doit obligatoirement faire partie d’une section. Si la section n'existe pas, en créer une. Les sections doivent apparaître dans l’ordre alphabétique pour plus de visibilité (option -G) et commencer par une majuscule. (les tests seront exécutés dans l’ordre indiqué).

 A noter que le test global (option -a) ne fait pas appel à ce fichier.

Création des fonctions de test unitaires

Quoi tester ?

Il faut tester toutes les fonctions présentes dans l’objet sur lequel les tests portent.

Exemple : un test qui porte sur la ClassMetierManagementCalculs.php devra tester toutes les fonctions présentes dans cette classe métier.

Dans l’idéal, il faut même prévoir plusieurs tests pour une même fonction.

Il faut tester ce qui doit fonctionner, mais aussi (et surtout !) ce qui ne devrait pas fonctionner : Le but principal des test unitaire est de s'assurer de la stabilité d’une API

Exemple avec la classe Metier ClassMeteirManagementDateAnsTime

Le classe à 18 methodes, simple, mais fait 43 tests !

On teste bien sur les formats de données attendus mais aussi le passage de dates erronés, mal formatés, langue inexistantes, etc...

Comment tester ?

Pour chaque fonction, il faut tester le plus de cas possibles. En pensant surtout, mais pas uniquement, aux cas extrêmes susceptibles de faire planter la fonction. Il faut aussi tester des cas pour lesquels on sait que la fonction va planter et donc vérifier qu’elle plante bien comme attendu.

Exemple : On souhaite réaliser des tests sur une fonction qui fait la division de deux nombres à virgule. On imagine que l’on a la fonction division(float a, float b) : float. On va donc tester au minimum :

  1. Le cas normal : tester avec 2 nombres, on teste que division(10.0, 5.0) = 2.0
  2. Le cas anormal : tester une division par 0, on teste que division(10.0, 0.0) lève bien une exception

Quelques cas usuels :

  1. La fonction a un argument qui peut avoir une valeur ou nulle

Dans ce cas là il va falloir faire au moins 2 tests, un avec une valeur correcte en entrée et une avec nulle, et tester que la fonction réagit comme on veut dans les deux cas.

  1. La fonction a un tableau en argument

Tester au moins 2 configurations de tableau en entrée, par exemple des données correctes et un tableau vide.

  1. La fonction attend une chaîne de caractère qui va être transformée en date

Tester avec une date normale et avec une chaîne de caractères qui ne correspond pas à une date. Ou une date formatée dans une autre langue.

Lors de la création de la fonction ne pas oublier de rajouter l’annotation @covers tel que recommandé par la documentation de PHPUnit 9 (voir : 2. Annotations — PHPUnit 9.6 Manual)

Nommage des fonctions de test unitaires

Chaque fonction de test unitaire doit respecter la nomenclature de nommage suivante en camelCase :

test<Nom fonction testée><description des conditions de test de la fonction>

Exemples :

Nom d’une fonction de test de division nommé division() avec deux float : testDivisionTwoFloat()

Nom d’une fonction de test de division nommé division() avec un float et un entier : testDivisionFloatInteger()

Nom d’une fonction de test de division nommé division() avec deux entier : testDivisionTwoInteger()

Nom d’une fonction de test de division nommé division() avec zéro : testDivisionByZero()

Tests Unitaires et bugs

Je lance mes tests unitaires et je trouve un bug, que faire?

En cas de doute, toujours prévenir un lead dev ou un chef de projet.

Je suis en développement

Si c’est dans le périmètre du projet, faire les corrections dans la méthode de la classe métier. Les tests servent à découvrir des bugs, c’est normal.

Si c’est hors du périmètre du projet, prévenir un lead dev ou un chef de projet, ce sera géré au cas par cas en fonction de la sévérité du bug.

Je suis en phase de test >= Q3

Si c’est dans le périmètre du projet, préparer un ticket de bugfix. Informer un chef de projet pour qu’il assigne le ticket.

Si c’est hors du périmètre du projet, prévenir un lead dev ou un chef de projet, se sera géré au cas par cas en fonction de la sévérité du bug.

Je rédige des tests unitaires pour des méthodes qui sont sur master

Prévenir un lead dev ou un chef de projet, se sera géré au cas par cas en fonction de la sévérité du bug.