mercredi 29 janvier 2014

Traitement du cas de test sans argument

A faire des fonctions utilisant la valeur par défaut  EMPTY  je réfléchissais aux tests des fonctions et du traitement du cas sans argument. Je ne peux pas laisser ce problème de côté, il faut le régler, je fais donc ce post pour améliorer le code de tests de fonctions une seconde fois. Le problème est que lorsque je mets une valeur par défaut, il faudrait tester le cas ou je ne passe pas l'argument avec valeur par défaut pour voir si le code qui doit gérer ce cas dans la fonction testée fonctionne bien. Or en passant la valeur par défaut, je ne teste pas forcément la portion de code qui y est dédiée surtout si j'emploie la valeur  EMPTY  que je ne peux stocker dans une variable, ni même passer en argument car le compilateur refuse. Nous allons donc régler ce problème: traiter le cas sans argument et pouvoir mettre la constante  EMPTY .

Utilisation de la constante EMPTY dans la fonction à tester:


Attention ! La constante  EMPTY  est à utiliser avec précautions. Je m'explique: elle possède une valeur entière valide qui est -1. Première conclusion, on ne peut pas l'utiliser avec les types  double  ou  string  (les autres types acceptant les nombres entiers). Deuxième remarque, c'est une valeur et il faut faire attention qu'elle ne soit pas utilisée parce que dans ce cas, quand on utilise la valeur -1 sans vouloir signifier une valeur vide, on se retrouve à traiter le cas de la valeur vide. Donc extrême vigilance. On ne touche donc pas au code de la première fonction obtenirNoDerniereSecConnue() qui a une valeur par défaut à  "NULL"  Cette valeur convient pour le cas, et  EMPTY  ne serait pas acceptée par le compilateur pour cause d'incompatibilité de type.

Création de directives #define pour des valeurs vides selon les types:


C'est bien beau de mettre la valeur vide par défaut, mais il va falloir trouver un moyen de signaler à la fonction de test qu'il n'y a pas d'argument car le compilateur ne va pas accepter une valeur vide dans une variable, ni dans un tableau. En informant la fonction de test qu'il n'y a pas de valeur, donc pas d'argument à mettre, elle peut faire une alternative d'appel de la fonction sans cet argument. Il faut alors trouver le moyen de lui indiquer. Ce moyen va être la définition de directives #define pour mettre dans les variables ou tableaux des constantes littérales. Ces constantes auront une valeur particulière dont on est certain (ou presque) qu'elles ne seront pas utilisées pour désigner le fait qu'il ne faut pas passer l'argument. Cette valeur est destinée à être stockée dans une variable ou un tableau, elle devra donc avoir le même type que l'argument. Il faut alors créer une directive #define par argument. Bien obligé d'utiliser une directive préprocesseur au lieu de variables constantes, car le compilateur ne les accepte pas dans les tableaux. Elles devront être utilisées dans tous les tests, ont les définit donc dans l'include UtilitairesTests.mqh. On ajoute le code suivant dans ce fichier:


//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+

#define  ARG_STRING_EMPTY "-#-EMPTY-#-"
#define  ARG_INT_EMPTY 2147483647
#define  ARG_BOOL_EMPTY -1
#define  ARG_DOUBLE_EMPTY 123456789.123456789
#define  ARG_DATETIME_EMPTY 4294967295
#define  ARG_COLOR_EMPTY 4294967295


Je propose ces valeurs car il y a une chaine de caractères très particulière dont il est très improbable de se servir dans les tests, la valeur positive maximum d'un entier, une valeur entière négative acceptée pour les booléens mais qui ne correspond ni à true (1) ni à false (0), une valeur décimale très grande et très particulière dont il est très improbable de se servir dans un test, une valeur entière pour datetime qui est la valeur maximum et la même valeur pour une couleur qui n'est pas utilisée car elle dépasse trois octets en mémoire (quatre octets tous remplis).

Prise en compte des cas de test où un argument est absent:


Maintenant que nous avons de quoi informer la fonction de test qu'il y a un argument à laisser vide, nous pouvons ajouter le code pour qu'elle fasse une alternative à l'exécution de la fonction à tester: avec ou sans l'argument. Pour ne pas modifier la boucle de test des différents cas, nous ajoutons juste un préfixe à la fonction appelée pour placer notre code supplémentaire dans une autre fonction. Ce préfixe est «exec» car la nouvelle fonction qu'on crée est là pour exécuter la fonction à tester de différentes manières: avec ou sans les arguments optionnels. Dans cette nouvelle fonction nous testons l'égalité de l'argument avec la constante correspondante précédemment définie pour excéuter comme il faut la fonction à tester: c'est-à-dire que si l'argument est égal à la constante désignant une valeur vide, alors la fonction est exécutée sans cet argument, sinon avec l'argument. Nous changeons aussi la valeur des arguments absents en mettant aussi cette valeur précédemment définie dans le tableau des arguments.

datetime execObtenirNoDerniereSecConnue(string argument) {
   datetime resultat;
   if(argument == ARG_STRING_EMPTY) { resultat = obtenirNoDerniereSecConnue(); }
   else { resultat = obtenirNoDerniereSecConnue(argument); }
   return(resultat);
}

int testObtenirNoDerniereSecConnue() {
   int noCas, nbErreurs = 0;
   datetime resultat;
   string arguments[NB_CAS_LAST_KNOWN_SEC_NO] = {ARG_STRING_EMPTY,
                                                 "EURUSD",
                                                 "GBPJPY",
                                                 "EUR",
                                                 ""};
   string parametresCas[NB_CAS_LAST_KNOWN_SEC_NO] = {" sans paramètre => ",
                                                     " \"EURUSD\" => ",
                                                     " \"GBPJPY\" => ",
                                                     " \"EUR\" => ",
                                                     " \"\" => "};
   datetime resultatsAttendus[NB_CAS_LAST_KNOWN_SEC_NO];
   resultatsAttendus[0] = TimeCurrent();
   resultatsAttendus[1] = MarketInfo("EURUSD", MODE_TIME);
   resultatsAttendus[2] = MarketInfo("GBPJPY", MODE_TIME);
   resultatsAttendus[3] = 0;
   resultatsAttendus[4] = 0;
   for(noCas=0 ; noCas<NB_CAS_LAST_KNOWN_SEC_NO ; noCas++) {
      resultat = execObtenirNoDerniereSecConnue(arguments[noCas]);
      nbErreurs += analyserResultatDatetimeDuTest(resultat,
                                                   resultatsAttendus[noCas],
                                                   nomObtenirNoDerniereSecConnue,
                                                   parametresCas[noCas]);
   }
   return(nbErreurs);
}



Le tutoriel vidéo et les deux fichiers de code complets:




//+------------------------------------------------------------------+
//|                                             UtilitairesTests.mqh |
//|                  Copyright 2014, argent-facile-avec-robots-forex |
//|               http://argent-facile-avec-robots-forex.blogspot.fr |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, argent-facile-avec-robots-forex"
#property link   "http://argent-facile-avec-robots-forex.blogspot.fr"

//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+

#define  ARG_STRING_EMPTY "-#-EMPTY-#-"
#define  ARG_INT_EMPTY 2147483647
#define  ARG_BOOL_EMPTY -1
#define  ARG_DOUBLE_EMPTY 123456789.123456789
#define  ARG_DATETIME_EMPTY 4294967295
#define  ARG_COLOR_EMPTY 4294967295

//+------------------------------------------------------------------+
//| constantes de tests                                              |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| fonctions                                                        |
//+------------------------------------------------------------------+

int analyserResultatDatetimeDuTest(
         datetime resultatTest,
         datetime resultatAttendu,
         string nomFonction,
         string parametresCas,
         string msgErreur = " a produit une erreur de résultat avec le cas ",
         string msgSucces = " a passé le test sans erreurs ") {
   int nbErreurs = 0;
   if(resultatTest == resultatAttendu) {
      Print(nomFonction, msgSucces, resultatTest);
   } else {
      Print(nomFonction, msgErreur, parametresCas, resultatTest);
      nbErreurs++;
   }
   return(nbErreurs);
}



//+------------------------------------------------------------------+
//|                                  testsArithmetiqueTemporelle.mqh |
//|                  Copyright 2014, argent-facile-avec-robots-forex |
//|               http://argent-facile-avec-robots-forex.blogspot.fr |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, argent-facile-avec-robots-forex"
#property link   "http://argent-facile-avec-robots-forex.blogspot.fr"

#include <UtilitairesTests.mqh>
#include <ArithmetiqueTemporelle.mqh>

//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+

#define NB_CAS_LAST_KNOWN_SEC_NO 5

//+------------------------------------------------------------------+
//| constantes de tests                                              |
//+------------------------------------------------------------------+

string nomObtenirNoDerniereSecConnue = "obtenirNoDerniereSecConnue()";

//+------------------------------------------------------------------+
//| fonctions de tests                                               |
//+------------------------------------------------------------------+

datetime execObtenirNoDerniereSecConnue(string argument) {
   datetime resultat;
   if(argument == ARG_STRING_EMPTY) { resultat = obtenirNoDerniereSecConnue(); }
   else { resultat = obtenirNoDerniereSecConnue(argument); }
   return(resultat);
}

int testObtenirNoDerniereSecConnue() {
   int noCas, nbErreurs = 0;
   datetime resultat;
   string arguments[NB_CAS_LAST_KNOWN_SEC_NO] = {ARG_STRING_EMPTY,
                                                 "EURUSD",
                                                 "GBPJPY",
                                                 "EUR",
                                                 ""};
   string parametresCas[NB_CAS_LAST_KNOWN_SEC_NO] = {" sans paramètre => ",
                                                     " \"EURUSD\" => ",
                                                     " \"GBPJPY\" => ",
                                                     " \"EUR\" => ",
                                                     " \"\" => "};
   datetime resultatsAttendus[NB_CAS_LAST_KNOWN_SEC_NO];
   resultatsAttendus[0] = TimeCurrent();
   resultatsAttendus[1] = MarketInfo("EURUSD", MODE_TIME);
   resultatsAttendus[2] = MarketInfo("GBPJPY", MODE_TIME);
   resultatsAttendus[3] = 0;
   resultatsAttendus[4] = 0;
   for(noCas=0 ; noCas<NB_CAS_LAST_KNOWN_SEC_NO ; noCas++) {
      resultat = execObtenirNoDerniereSecConnue(arguments[noCas]);
      nbErreurs += analyserResultatDatetimeDuTest(resultat,
                                                   resultatsAttendus[noCas],
                                                   nomObtenirNoDerniereSecConnue,
                                                   parametresCas[noCas]);
   }
   return(nbErreurs);
}

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int init()
  {
   int nbErreurs = 0;
   nbErreurs += testObtenirNoDerniereSecConnue();
   Print("TESTS DE ArithmetiqueTemporelle.mqh TERMINES AVEC ", nbErreurs, " ERREUR(S).");
   return(0);
  }

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+

int deinit()
  {
   return(0);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+

int start()
  {
   return(0);
  }

//+------------------------------------------------------------------+


Aucun commentaire:

Enregistrer un commentaire