WinDev : Le développement par les tests

| V.Formet | LittleWhite |

La conception d'un logiciel, site Internet, web service, API… comporte différentes étapes :

- analyse ;

- spécifications techniques et fonctionnelles ;

- développement ;

- tests (unitaires, intégrations, validations) ;

- recettes ;

- maintenance.

Cette liste n'est pas exhaustive, elle peut varier, être réduite ou complétée selon le contexte technique, client, budgétaire… La conception d'un logiciel et ses étapes s'orientent autour du cycle de développement, il en existe plusieurs :

- modèle en cascade ;

- cycle en V ;

- cycle en spirale ;

- cycle semi-itératif ;

- cycle itératif.

Chaque cycle possède ses avantages et ses inconvénients, ainsi que des défenseurs et des détracteurs. Ils ont tous un point commun, placer en premier l'écriture du code avant la phase de tests dans le cycle de développement. Cet ordre chronologique paraît logique, on teste ce que l'on vient de produire, pourtant une autre technique préconise l'écriture des tests avant l'écriture de l'application. Dans cet article, nous allons voir une application du développement par les tests dans l'EDI WinDev. ♪

Commentez

- Titre : WinDev : Le développement par les tests

- Auteurs : V.Formet, LittleWhite

- Parution : 24 avril 2015

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2015 V.Formet. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Le développement par les tests

- II. Contexte technique

- III. Les tests automatiques en WinDev

- IV. TDD

- V. Conclusion

- VI. Remerciements

I - Le développement par les tests

Commençons cet article par la définition du TDD (source Wikipédia) : Le Test Driven Development (TDD) ou en français le développement piloté par les tests est une technique de développement de logiciel qui préconise d'écrire les tests unitaires avant d'écrire le code source d'un logiciel.

Contrairement à d'autres cycles de développement logiciel (modèle en cascade, cycle itératif) plaçant les tests en fin de cycle, le TDD place les tests en première place dans le cycle de développement. C'est une méthode dans laquelle l'écriture des tests automatisés dirige l'écriture du code source.

Les étapes du développement par les tests

La mise en place de la méthode TDD offre de nombreux avantages au sein du développement d'un logiciel. Voici les avantages apportés par l'emploi de cette méthode.

Les tests unitaires sont réellement écrits

Comme évoqué dans les autres cycles de développement, les tests sont effectués à la fin d'un projet. D'après plusieurs enquêtes, 70 % des projets informatiques se terminent en retard, pour diminuer ce retard la phase de test est souvent réduite au strict minimum. Le fait de commencer par rédiger les tests permet de s'assurer que les tests seront écrits.

Fiabilité du code

En suivant la méthode traditionnelle, le développeur écrit une fonctionnalité et va ensuite procéder aux tests afin de s'assurer que ce qu'il a codé est valide. Mais inconsciemment le développeur teste son code comme il l'a conçu, le nombre de bogues trouvés est peu important et le risque de passer à côté de bogues est élevé.

Analyse détaillée et plus précise

En effet, lorsque le développeur écrit du code de test pour tester une implémentation qui n'existe pas encore, il va devoir penser aux détails de la méthode dont il a besoin pour écrire la spécification. Aussi, il va alors s'interroger sur le nom de la méthode, sa valeur de retour, ses paramètres, son comportement…, cela permet de clarifier la conception et d'écrire seulement du code utile.

Vérification démontrable, répétable et automatisée

Le fait de disposer d'un grand nombre de tests permet de s'assurer de la solidité et garantie du code.

Absence de régression

Lorsqu'un développeur modifie une méthode existante lors d'une phase de factorisation, il peut relancer les tests unitaires afin de s'assurer que sa modification n'a pas impacté l'existant et bénéficie d'un retour immédiat.

Couplage plus faible et conception simplifiée

Le TDD impose d'écrire le code des tests en peu de temps ce qui amène le développeur à concevoir du code avec un faible couplage et avec un niveau relativement simple.

Malgré les avantages listés ci-dessus, il faut bien comprendre que le TDD n'est pas une réponse miracle aux problèmes de développements dans les équipes, il n'empêche pas les bogues et les erreurs d'analyse, de conception… Quand on évoque le TDD, un inconvénient revient souvent :

Plus de travail

Depuis longtemps le développeur n'aime pas effectuer les tests, il trouve cette partie du travail fastidieuse, non valorisante et limite hors cadre de sa fonction. En écrivant des tests unitaires, le développeur a plus de travail.

II - Contexte technique

Windev : Version 20 et antérieure

Programmation : POO, test unitaire.

III - Les tests automatiques en WinDev

Pour lancer les tests automatiques dans WinDev, sélectionner l'option Enregistrer un nouveau test dans le menu Tests automatiques :

Menu Windev des tests automatique

WinDev affiche une fenêtre proposant automatiquement de lancer votre projet et d'enregistrer les opérations graphiques. Cela permet de simuler des tests de vos interfaces. Dans le cadre de cet article, nous ne nous intéressons pas à cette fonctionnalité, cliquez sur le bouton annuler.

Assistant des tests automatiques proposé par WinDev

WinDev a créé un test vierge : Scénario1.

On ne va pas perdre les bons réflexes de programmation même pour des tests, on va donner un nom et une description à notre test. Effectuer un clic droit sur le test Scénario 1 et sélectionner l'option Description.

Menu des options des tests automatiques

Modifier le nom et ajouter une description.

Modification du nom du test

Maintenant, place à la programmation de nos tests, pour cela nous allons ajouter du code, clic droit, sélectionner l'option 'Code' :

Sélection de l'option code

Une fenêtre s'ouvre en style éditeur de code, la suite de ce tutoriel continuera au sein de cet éditeur.

IV - TDD

Dans le cadre de cet article, nous allons suivre étape par étape le développement d'une fonction calculant la date de fin d'un contrat de garantie.

- Nom de la procédure : proc_CalculDateFinContrat

- Paramètre 1 : date de début

- Paramètre 2 : durée de la garantie en mois

- Descriptif : ajoute de la durée à la date de début

Création du premier test : dans ce test, nous appelons la fonction et nous testons que la date n'est pas modifiée si la durée est à 0.

Premier Test

SI proc_CalculDateFinContrat("19000101",0) = "19000101" ALORS
TestEcritRésultat(terInfo,"Test OK de la date 01/01/1900 et 0 mois")
SINON
TestEcritRésultat(terErreur,"Erreur sur le test de la date 01/01/1900 et 0 mois")
FIN

Lançons maintenant le test, comme prévisible il échoue. Normal, nous n'avons pas créé la fonction gproc_CalculDateFinContrat.

Rejet du test

Créons la procédure :

Code de la procédure (version 1)

// Résumé : Calcule la date de fin de contrat
// Syntaxe :
//[ <Résultat> = ] gproc_CalculDateFinContrat (<pd_dateDebutContrat> est date, <pi_nbMois> est entier)
//
// Paramètres :
//    pd_dateDebutContrat (date) : Date de début de contrat
//    pi_nbMois (entier) : durée de contrat exprimée en mois
// Valeur de retour :
//     Date : Date de fin de contrat
//
// Exemple :
// gproc_CalculDateFinContrat("20150401",12)
//
PROCEDURE gproc_CalculDateFinContrat(pd_dateDebutContrat est une Date,pi_nbMois est un entier)
//----->Déclaration des variables
ls_dateTemp est une chaîne

ls_dateTemp=DateVersChaîne(Pd_dateDebutContrat,"AAAAMMJJ")

RENVOYER ChaîneVersDate(ls_dateTemp,"AAAAMMJJ")

Remarque : Depuis la version 20, WinDev permet de typer le paramètre de retour.

Exemple de l'entête de la fonction avec le paramètre de retour typé

PROCEDURE gproc_CalculDateFinContrat(pd_dateDebutContrat est une Date,pi_nbMois est un entier) : date

Relançons le test et constatons son succès :

Test validé

Maintenant que nous avons créé le premier test et la procédure qui permet de vérifier le retour d'une date, nous allons coder l'incrémentation des mois et des années. Dans le code de notre test, nous ajoutons les trois cas suivants :

Test : Ajout de nouveaux tests

SI gproc_CalculDateFinContrat("20150115",1) = "20150215" ALORS
TestEcritRésultat(terInfo,"Test OK de la date 15/01/2015 et 1 mois")
SINON
TestEcritRésultat(terErreur,"Erreur sur le test de la date 15/01/2015 et 1 mois")
FIN

SI gproc_CalculDateFinContrat("20150115",12) = "20160115" ALORS
TestEcritRésultat(terInfo,"Test OK de la date 15/01/2015 et 12 mois")
SINON
TestEcritRésultat(terErreur,"Erreur sur le test de la date 15/01/2015 et 12 mois")
FIN

SI gproc_CalculDateFinContrat("20150115",18) = "20160715" ALORS
TestEcritRésultat(terInfo,"Test OK de la date 15/07/2015 et 18 mois")
SINON
TestEcritRésultat(terErreur,"Erreur sur le test de la date 15/01/2015 et 18 mois")
FIN

Lançons le test et constatons son échec :

Rejet des tests ajoutés

Nous revenons dans le code de notre procédure pour développer l'incrémentation de la date, le développeur fait le choix de découper la date en entrée en trois variables :

- li_jour : type entier pour gérer les jours ;

- li_mois : type entier pour gérer les mois ;

- li_annee : type entier pour gérer les années.

Le code de la procédure avec la prise en charge de l'incrémentation,

Code de la procédure (version 2)

PROCEDURE gproc_CalculDateFinContrat(pd_dateDebutContrat est une Date,pi_nbMois est un entier)
//----->Déclaration des variables
ls_dateTemp est une chaîne
li_jour, li_mois, li_annee est un entier

ls_dateTemp=DateVersChaîne(pd_dateDebutContrat,"AAAAMMJJ")
//----->Découpage de la date en 3 parties
li_jour=Val(Droite(ls_dateTemp,2))
li_mois=Val(Milieu(ls_dateTemp,5,2))
li_annee=Val(Gauche(ls_dateTemp,4))

//----->Ajout des mois
li_mois+=modulo(pi_nbMois,12)
li_annee+=PartieEntière(pi_nbMois/12)

//----->Concaténation des 3 parties
ls_dateTemp=NumériqueVersChaîne(li_annee)+NumériqueVersChaîne(li_mois,"02D")+NumériqueVersChaîne(li_jour,"02D")

RENVOYER ChaîneVersDate(ls_dateTemp,"AAAAMMJJ")

Relançons le test automatique et constatons son succès :

Test 2 validé

Le développement avance, on a l'incrémentation des mois et des années, maintenant place à la gestion des fins de mois.

Exemple : un contrat qui commence le 31/01/2015 pour une durée de trois mois se finira le 30/04/2015, même problème pour le mois de février, et attention aux années bissextiles. Ajoutons dans le code de notre test le code suivant :

Test : ajout de nouveaux tests

SI gproc_CalculDateFinContrat("20150131",3) = "20150430" ALORS
TestEcritRésultat(terInfo,"Test OK de la date 31/01/2015 et 3 mois")
SINON
TestEcritRésultat(terErreur,"Erreur sur le test de la date 31/01/2015 et 3 mois")
FIN

SI gproc_CalculDateFinContrat("20150131",1) = "20150228" ALORS
TestEcritRésultat(terInfo,"Test OK de la date 31/01/2015 et 1 mois")
SINON
TestEcritRésultat(terErreur,"Erreur sur le test de la date 31/01/2015 et 1 mois")
FIN

SI gproc_CalculDateFinContrat("20150131",13) = "20160229" ALORS
TestEcritRésultat(terInfo,"Test OK de la date 31/01/2015 et 13 mois")
SINON
TestEcritRésultat(terErreur,"Erreur sur le test de la date 31/01/2015 et 13 mois")
FIN

Constatons l'échec des tests :

Les tests ajoutés pour la version 3 sont rejetés

Revenons au code de notre procédure et mettons en place la gestion de fin de mois :

Code de la procédure avec la prise en compte du dernier jour du mois

PROCEDURE gproc_CalculDateFinContrat(pd_dateDebutContrat est une Date,pi_nbMois est un entier)

//----->Déclaration des variables
ls_dateTemp est une chaîne
li_jour, li_mois, li_annee est un entier

ls_dateTemp=DateVersChaîne(pd_dateDebutContrat,"AAAAMMJJ")
//----->Découpage de la date en 3 parties
li_jour=Val(Droite(ls_dateTemp,2))
li_mois=Val(Milieu(ls_dateTemp,5,2))
li_annee=Val(Gauche(ls_dateTemp,4))

//----->Ajout des mois
li_mois+=modulo(pi_nbMois,12)
li_annee+=PartieEntière(pi_nbMois/12)

SI li_jour=31 _ET_ li_mois _DANS_(4,6,9,11) ALORS
li_jour=30
SINON SI li_jour>28 ET li_mois=2 ALORS
SI AnnéeBissextile(li_annee) ALORS
li_jour=29
SINON
li_jour=28
FIN
FIN

//----->Concaténation des 3 parties
ls_dateTemp=NumériqueVersChaîne(li_annee)+NumériqueVersChaîne(li_mois,"02D")+NumériqueVersChaîne(li_jour,"02D")

RENVOYER ChaîneVersDate(ls_dateTemp,"AAAAMMJJ")

Relançons les tests et constatons le bon déroulement :

Test 3 validé

Le développement par les tests prévoit une phase de factorisation. Après avoir parcouru la documentation, on prend note de l'existence du type date et surtout des possibilités d'effectuer des calculs sur les parties de celle-ci (incrémentation et décrémentation des jours, mois et années).

Voilà le code factorisé (dans ce cas on parle plus d'amélioration de code) :

Code de la procédure (étape de factorisation)

PROCEDURE gproc_CalculDateFinContrat(p_dateDebutContrat est une Date,pi_nbMois est un entier)

//----->Déclaration des variables
ld_dateTemp est une Date

ld_dateTemp=pd_dateDebutContrat

ld_dateTemp..Mois+=pi_nbMois

RENVOYER ld_dateTemp

Nous avons modifié le code, nous devons nous assurer que celui-ci n'engendre pas de régressions. Pour cela, nous allons relancer nos tests et constater que ceux-ci fonctionnent.

Test validé après factorisation du code

Et voilà, nous venons de développer notre fonction en utilisant le TDD.

V - Conclusion

Dans cet article, nous avons décrit le cycle de développement par les tests en le mettant en application avec un exemple. La question que l'on peut se poser, est-ce que cette méthode est applicable pour l'ensemble des développements d'un projet ? Personnellement, je vois un intérêt à la mettre en place pour le développement :

- des fonctions métiers ;

- de la gestion de base de données ;

- des échanges de données avec des API, web services, Middleware.

Mais en ce qui concerne l'IHM, la validation par une personne physique reste la plus efficace. Effectivement, l'automatisation nous permet de valider le bon déroulement d'un traitement, mais il ne nous mettra pas en garde sur des problèmes d'utilisation :

- nombre de clics trop important ;

- texte pas intuitif ;

- les champs non alignés ;

- choix des couleurs ;

- …

Après la validation d'une personne, il est possible d'automatiser les tests des IHM pour vérifier

- la non-régression ;

- les erreurs de suppressions ou de changements de nom de champs.

Mais cette méthode ne rentre pas dans le cadre de la mise en place du TDD.

VI - Remerciements

Remerciements :

- relecture technique : LittleWhite;

- relecture orthographique : f-leb ;

- vérification avant mise en ligne : djibril.