Modifier

Le contexte

Il y a quelques jours, je travaillais pour un de mes clients, et je devais intervenir sur une fenêtre pour laquelle je ne pouvais pas utiliser la base de données (je ne l'ai jamais paramétré chez moi). Je me suis souvenu que chaque fois que les développeurs travaillaient sur ce projet, le temps de lancement du go pouvait prendre plusieurs dizaines de seconde avant d'afficher quoique ce soit. On a même cru que la fenêtre buggait tellement elle mettait de temps à s'afficher.

J'aurais pu paramétrer la base de données comme tout le monde et tomber dans un monde où un simple test met une plombe à se lancer. Mais vous commencez peut-être à me connaître, je suis fainéant et je n'aime pas attendre. J'ai donc réfléchi à un moyen d'avoir une fenêtre qui s'ouvre rapidement sans cette fichue base de données.

J'ai donc appliqué le principe D de SOLID (si vous ne connaissez toujours pas, cliquez ici et lisez le paragramhe DIP) et j'ai réfléchi à comment inverser les dépendances et que ma fenêtre ne dépende plus de cette base.

Avant de rentrer dans le côté technique, il faut savoir que cette méthode me permet aussi de tester plusieurs scénarios sur ma fenêtre. Je peux tester ma fenêtre avec aucun enregistrement dans la base de données, avec 10, 20, 100 et ajouter tous les scénarios qui me viennent à l'esprit. Je peux simuler une erreur de la base de données au moment de l'ajout par exemple.

Par contre, cette technique peut se révéler compliquée à mettre en place si vous travaillez directement avec des tables reliées à un fichier. En effet, il faut enlever les dépendances et ce lien est une dépendance. Il faudra le faire sauter si vous souhaitez appliquer cette technique.

Bien, parlons techniques.

La technique

tl;dr :

L'idée de base est d'utiliser la compilation conditionnelle et le polymorphisme pour séparer le cas réel du cas de test. Et si vous n'avez pas envie de lire, vous pouvez suivre la formation vidéo (plus d'information dans la conclusion).

Vous êtes toujours là ? C'est parti !

1. Extraire toutes les dépendances

On commence par créer une classe. Personnellement, avec ma convention de langage, je l'appelle c + le nom de la fenêtre + le préfixe final (c'est une convention temporaire).

Nom de fenêtre : wdClient
Nom de classe : cWdClientFinal

Ensuite, j'instancie cette classe dans les globales de la fenêtre

PROCEDURE MaFenêtre()
comportement is cWdClientFinal dynamic = créerComportement()

ainsi que la procédure créerComportement

PROCEDURE créerComportement()
RENVOYER allouer cWdClientFinal()

Et je parcours toute ma fenêtre pour trouver le code qui dépend d'un élément externe.

Si par exemple, j'ai le code suivant dans le champ d'initialisation de la fenêtre :

Nom = Client.Nom // Nom est un champ de saisie et Client est un fichier HF SQL

Je peux le remplacer par

Nom = comportement.obtenirNom()

et je crée la méthode obtenirNom dans la classe cWdClientFinal:

PROCEDURE obtenirNom()
RENVOYER Client.Nom

Je fais ça pour tous les appels à la base de données, ainsi que toutes les dépendances qui sont complexes à gérer.

Ensuite, vous pouvez tester votre fenêtre, elle devrait fonctionner comme avant.

2. Extraire une interface

Après tout ça, on va faire en sorte de remplacer ce comportement par un autre comportement. Pour cela, on va utiliser le polymorphisme. Ce mot vous fait peut-être peur, mais vous allez voir, la mise en place est toute simple.

On commence par remplacer le type de notre variable comportement (pour rappel, c'était un cWdClientFinal dynamic) par un nouveau type (qui n'existe pas encore) : cWdClient. N'oubliez pas de changer l'opérateur d'affectation.

PROCEDURE MaFenêtre()
comportement is cWdClient <- créerComportement() 

Le compilateur vous crie dessus parce que cWdClient n'existe pas. On va donc lui faire plaisir et le créer. Et ce nouveau type, ça va être une interface que vous pouvez mettre dans le code du projet, collection de procédures, etc...

cWdClient est interface
Fin

Après cela, le compilateur va crier quelque chose de différents. Il va vous dire que les méthodes n'existent pas dans le type cWdClient. C'est normal puisqu'elles n'existent que dans la classe cWdClientFinal. On va donc rajouter dans l'interface toutes les méthodes qui manquent. Vous devez indiquer exactement la même syntaxe que celles de la classe cWdClientFinal.

cWdClient est interface
    Procédure obtenirNom()
FIN

Si vous le souhaitez, vous pouvez indiquer que cWdClientFinal implémente cette interface. Mais cette étape est optionnelle (quoique conseillée).

cWdClientFinal est une classe
    implémente cWdClient
FIN

Et voilà, si vous avez fait les choses correctement, votre fenêtre fonctionne toujours de la même manière (beaucoup de travail pour ne rien changer).

3. Implémenter le nouveau comportement

Les choses sérieuses commencent maintenant. Nous allons voir pour implémenter le nouveau comportement à notre fenêtre. Et là encore, ça reste simple.

Nous allons créer une classe cWdClientForTest qui va implémenter l'interface cWdClient.

cWdClientForTest est une classe
    implémente cWdClient
FIN

Le compilateur va nous indiquer qu'il faut créer toutes les procédures dans cette nouvelle classe, ce que nous allons faire.

PROCEDURE obtenirNom()
RENVOYER "Roger"

Et voilà, notre nouveau comportement est prêt à être utilisé

4. Utiliser le nouveau comportement (ou l'ancien)

Maintenant, nous voulons utiliser le nouveau comportement à la place de l'ancien, mais nous souhaitons aussi que l'ancien fonctionne toujours. C'est normal, c'est lui que nous souhaitons livrer au client.

Pour cela, nous allons utiliser les configurations de projet et créer la configuration Test. Nous mettons dans cette nouvelle configuration la fenêtre wdClient, la collection de procédures contenant l'interface cWdClient et la classe cWdClientForTest. Nous pouvons d'ailleurs exclure cette classe de la configuration principale.

Enfin, nous modifions le code de la procedure créerComportement.

PROCEDURE créerComportement()
<COMPILE SI Configuration="Développement">
    RENVOYER allouer cWdClientForTest()
<SINON>
    RENVOYER allouer cWdClientFinal()
<FIN>

Et voilà, pour changer de comportement, il suffit de changer de configuration de projet.

5. Gérer des scénarios différents pour vos tests

Vous souhaitez maintenant gérer des scénarios différents ? Vous pouvez utiliser la ligne de commande et ajouter un nouveau paramètre scénario. En fonction de ce paramètre, votre comportement de test changera.

PROCEDURE obtenirNom()
SELON LigneCommande("Scenario") :
    CAS "Avec Robert"
        Renvoyer Robert
    AUTRE CAS
        RENVOYER "Roger"
FIN

Conclusion

Le test est toujours un art difficile, surtout celui des IHM. Mais avec cette technique, vous pouvez vous simplifier énormément la vie, pour peu que vous preniez le temps de mettre ces tests en place.

Imaginez, vous rencontrez un bug chez un client, il vous suffit de mettre en place le scénario adéquat pour le reproduire et le corriger. Vous pouvez aussi vous servir de cette méthode pour développer votre écran puis implémenter la partie base de données plus tard. La seule contrainte, c'est le temps d'apprentissage.

C'est pourquoi, en plus de cet article, je vous propose une formation vidéo pour maîtriser cette technique encore plus vite. Elle contiendra des manipulations, plus de théorie, plus de pratique ainsi qu'un projet exemple. Si vous êtes intéressés, je vous invite à aller voir la page de la formation : WinDev - Testez vos fenêtres sans vous prendre la tête.

Merci d'avoir lu cet article jusqu'au bout. J'espère qu'il vous inspirera et vous permettra de produire un code de meilleure qualité.

Je compte sur vous pour me donner votre avis, poser vos questions par commentaire.

A bientôt

Article suivant Article précédent

Blog Comments powered by Disqus.