La présentation est en train de télécharger. S'il vous plaît, attendez

La présentation est en train de télécharger. S'il vous plaît, attendez

Test et débogage Tests unitaires. Gestion derreurs. Notion détat, de pré-condition et de post-condition. Assertion. Traces de programme. Débogueur et ses.

Présentations similaires


Présentation au sujet: "Test et débogage Tests unitaires. Gestion derreurs. Notion détat, de pré-condition et de post-condition. Assertion. Traces de programme. Débogueur et ses."— Transcription de la présentation:

1 Test et débogage Tests unitaires. Gestion derreurs. Notion détat, de pré-condition et de post-condition. Assertion. Traces de programme. Débogueur et ses limites. Stratégies de mise au point du programme. Normes de programmation.

2 2 La gestion des erreurs Plusieurs problèmes peuvent survenir pendant lexécution dun programme: - insuffisance de mémoire, disque plein, - perte dun fichier, imprimante non branchée ou à court de papier, - saisie non valide dune valeur, - une fonction qui fonctionne mal, - etc. Rôle des programmeurs face à ces erreurs dexécution: - prévoir ces erreurs, - une application étant construite à partir dun enchaînement dappels de fonctions, toute fonction pouvant être confrontée à une erreur dexécution, il devient nécessaire de pouvoir gérer les appels (les erreurs) en cascade de fonctions, - en informer lutilisateur, sauver son travail et arrêter le programme de façon contrôlée, - éventuellement, de mettre en œuvre des solutions de reprises et de correction.

3 3 Mise en œuvre de la gestion des erreurs Ignorer lerreur Utiliser une variable globale qui sera initialisée par la fonction provoquant lerreur dexécution. Le contenu de cette variable sera ensuite récupéré et traité par la fonction appelante de celle qui a entraîné lerreur dexécution. Les variables globales noffrent aucune garantie de sécurité. Cela signifie que lon peut y accéder de nimporte où sans restriction.**** à éviter **** PROBLÈME RENCONTRÉ : le code qui détecte lerreur ne peut rien faire pour sauver le travail de lusager et quitter élégamment. Programmation défensive Prendre la décision de ne rien faire en cas de demande erronée. Ex. : Ne pas insérer un nouvel élément dans une pile pleine. Dans certains cas, on ne peut ignorer lerreur parce que lopération ne peut être complétée.Ex. : lors dun calcul. Peut masquer des erreurs importantes. Supposer que les erreurs ne se produiront pas est une mauvaise approche.

4 4 Mise en œuvre de la gestion des erreurs Imprimer un message derreur et/ou arrêter le programme - Le code de traitement des erreurs est dispersé et imbriqué tout au long du code du système. - Les erreurs sont gérées là où ces erreurs sont le plus susceptibles de se produire. Avantage:En lisant le code, on peut voir le traitement derreur dans le voisinage immédiat du code et déterminer si une vérification adéquate des erreurs a été mise en place. Désavantage:Le code est « pollué » par le traitement derreurs. Cela rend le code plus difficile à comprendre et à maintenir. Envisageable au cours de la phase de mise au point seulement. **** à éviter ****

5 5 Mise en œuvre de la gestion des erreurs Syntaxe : assert(expression logique); Donne lieu à aucune action si lexpression logique est vraie. Autrement, le programme termine anormalement en affichant un diagnostic. Note : Cette clause fait partie de la librairie C : assert.h Assertion failed: n > 0, file e:\nouveau_dossier\chiffres_romains.c, line 6 #include int sommation(int n) { assert(n > 0);return n * (n + 1) / 2; } void main() { int N; printf("Entrez un nombre entier positif : "); scanf("%i", &N); printf("\nSommation : %i", sommation(N)); } -5

6 6 Mise en œuvre de la gestion des erreurs AbandonnerRecommencerIgnorer Débogage Envoyer le rapport derreurs Ne pas envoyer Le programme prend fin Le programme prend fin avec un message quil termine anormalement. Mettre fin à lexécution est souvent une méthode trop radicale pour répondre à une erreur. De nombreuses erreurs peuvent être gérées si elles sont signalées de façon à ce quun programmeur puisse les détecter et les analyser.

7 7 Mise en œuvre de la gestion des erreurs En C++, on utilise la librairie cassert. #include int sommation(int n) { assert(n > 0); return n * (n + 1) / 2; } void main() { int N; cout << "Entrez un nombre entier positif : "; cin >> N; cout << "\nSommation : " << sommation(N); cout << "\nFIN\n"; }

8 8 Mise en œuvre de la gestion des erreurs Approche classique dinterception derreurs: retour de code derreur - des fonctions symbolisent les traitements à mettre en œuvre; - ces fonctions renvoient des valeurs qui peuvent servir à déterminer le succès ou léchec du traitement; - on peut tester les valeurs de retour des fonctions et réagir en conséquence aux erreurs dexécution.

9 9 Utilisation des valeurs de retour #include int Fonction_1(………….) { ……... // Retourne 0 si aucune erreur nest détectée; // un entier positif autrement. …….. } ………... int Fonction_m(………….) { ……... // Retourne 0 si aucune erreur nest détectée; // un entier positif autrement. …….. }

10 10 Utilisation des valeurs de retour int main() { ………. // Appel de la fonction Fonction_i. // Si une erreur est détectée, alorsgérer cette erreur en lien avec la fonction Fonction_i sinonappel de la fonction Fonction_j si une erreur est détectée, alors gérer cette erreur en lien avec Fonction_j sinon ……….. ………. } Cela met en évidence larchitecture nécessaire à des appels de fonctions en cascade qui peuvent entraîner une erreur dexécution. Architecture peu lisible et surtout très pénible à maintenir (if-else imbriqué qui alourdit lécriture et la mise à jour du programme).

11 11 Utilisation des valeurs de retour #include enum Nom_des_fonctions {Fn_A, Fn_B}; int Fonction_A(float v) { if (v < 0.0) return 1; if (v == 0.0) return 2; // Traitement. return 0; } int Fonction_B(char c) { if (c == ' ') return 1; if ((c 'Z')) return 3; // Traitement. return 0; }

12 12 Utilisation des valeurs de retour int Gestion_des_erreurs(Nom_des_fonctions Nom, int code_erreur) { if (Nom== Fn_A) switch(code_erreur) { case 0 : cout << "Parfait";return 0; case 1 : cout<<"Erreur 1"; return 1; case 2 : cout << "Erreur 2"; return 0; default: cout << "Erreur"; return 0; } else if (Nom== Fn_B) switch(code_erreur) { case 0 : cout << "Parfait";return 0; case 3 : cout <<"Erreur 3"; return 1; // Erreur grave default: cout << "Erreur"; return 0; } else cout << "incomplet ";return 0; }

13 13 Utilisation des valeurs de retour int main() { char C = ' '; float U = -3.2f; if (Gestion_des_erreurs(Fn_A, Fonction_A(U))) return 1; if (Gestion_des_erreurs(Fn_B, Fonction_B(C))) return 1; return 0; }

14 14 Mécanisme des exceptions Solution fiable et standardisée de gestion derreurs dexécution qui ne se fonde pas sur les valeurs de retour des fonctions. - permet de capturer toutes les exceptions dun type donné, - permet de lever des exceptions et transférer le contrôle avec les informations pertinentes à une autre partie du code qui pourra gérer la situation correcte- ment, - en retirant le code de traitement des erreurs du « flot principal » dexécution dun programme, cela permet daméliorer la lisibilité des programmes et faciliter leur maintenance. - permet de gérer nativement les appels de fonctions en cascade.

15 15 Test unitaire dune fonction Voici une fonction qui calcule la racine carrée dun nombre entier positif ou nul avec une précision EPSILON : Exemple : #include const float EPSILON = f; // Calcule la racine carrée de la valeur réelle a. // // Paramètre d 'entrée : //a: une valeur réelle // // Valeur de renvoi : //la racine carrée de a. Au lieu dassembler chaque composante dun programme en espérant que cela fonctionne, il est préférable de les tester individuellement. Le test unitaire (ou test de la boîte noire) est un programme avec un résultat booléen qui ne se préoccupe pas du fonctionnement interne de la fonction.

16 16 Test unitaire dune fonction float RacineCarree(float a) { if (a < 0.0f) throw(1);// Lancement dune exception. if (a == 0.0f) return 0.0f; float xnouveau = a; float xvieux; do { xvieux = xnouveau; xnouveau = (xvieux + a / xvieux) / 2.0f; } while ((xnouveau - xvieux) >= EPSILON || (xnouveau - xvieux) <= - EPSILON ); return xnouveau; }

17 17 Test unitaire dune fonction void main() { float x, y, z; // Test positif où RacineCarree doit fonctionner correctement. x = 2.0f; y = RacineCarree(x); z = y * y; if ((z - x) = - EPSILON) cout << "succes\n"; else cout << "echec\n"; // Test frontière où RacineCarree doit fonctionner correctement. x = 0.0f; y = RacineCarree(x); z = y * y; if ((z - x) = -EPSILON) cout << "succes\n"; else cout << "echec\n"; x = EPSILON; y = RacineCarree(x); z = y * y; if ((z - x) = -EPSILON) cout << "succes\n"; else cout << "echec\n";

18 18 Test unitaire dune fonction // Test négatif où la fonction RacineCarree doit échouer. try { x = -1.0f; y = RacineCarree(x); cout << "echec\n"; } catch(int i)// Prise en compte dune exception. { cout << i << " : succes\n"; }

19 19 Test unitaire dune fonction #include /*Le président d'un syndicat local possède l'information suivante sur ses membres (dont le nombre ne dépassera jamais 100) : -le numéro d'employé (lequel est une identification unique de l'employé correspondant à un nombre de 4 chiffres dont le 1er est 6), -son nom, -son prénom, -son adresse, -son traitement annuel -sa cotisation syndicale annuelle (entre 0 et 2%).*/ struct employe { int numero_employe; char nom[20+1], prenom[20+1], adresse[40+1]; float traitement_annuel;float cotisation_annuelle; } ensemble_membres[100]; int Nombre_de_membres; /* Renferme le nombre de syndiqués.*/

20 20 Test unitaire dune fonction void syndicat() //Permet de créer un syndicat avec aucun membre. { Nombre_de_membres = 0; } int Nombre_de_membres_du_syndicat() /*Permet de connaître le nombre de membres faisant partie du syndicat. Valeur de renvoi : le nombre de membres du syndicat.*/ { return Nombre_de_membres; }

21 21 Test unitaire dune fonction float Calcule_cotisation_annuelle_moyenne() /*Fournit la moyenne des cotisations annuelles des membres. Le syndicat possède au moins un membre. Valeur de renvoi : la moyenne des cotisations annuelles.*/ { float somme = 0.0f; if (Nombre_de_membres == 0) throw(3); for (int i = 0; i < Nombre_de_membres; i++) somme += ensemble_membres[i].cotisation_annuelle; return somme / Nombre_de_membres; }

22 22 Test unitaire dune fonction void Inserer_nouveau_membre(int numero_employe, char * nom, char * prenom, char * adresse, float traitement_annuel, float cotisation_annuelle) /* Permet d'ajouter un nouveau membre syndiqué dont les caractéristiques sont passées en paramètres. Le nombre d'employés est moindre que 100. L'employé ayant ce numéro n'est pas syndiqué jusqu'à maintenant. Paramètres d'entrée : numero_employe : le no. de l'employé entre 6000 et 6999, nom : le nom de l'employé, prenom : le prénom de l'employé, adresse : son adresse, traitement_annuel : le traitement annuel de l'employé, cotisation_annuelle : la cotisation annuelle entre 0 et 2. */

23 23 Test unitaire dune fonction { if (Nombre_de_membres == 100) throw(4); for (int i = 0; i < Nombre_de_membres; i++) if (ensemble_membres[i].numero_employe == numero_employe) throw(4); if ((numero_employe 6999)) throw(4); if((cotisation_annuelle 2)) throw(4); ensemble_membres[Nombre_de_membres].numero_employe = numero_employe; strcpy(ensemble_membres[Nombre_de_membres].nom, nom); strcpy(ensemble_membres[Nombre_de_membres].prenom, prenom); strcpy(ensemble_membres[Nombre_de_membres].adresse, adresse); ensemble_membres[Nombre_de_membres].traitement_annuel = traitement_annuel; ensemble_membres[Nombre_de_membres].cotisation_annuelle = cotisation_annuelle; Nombre_de_membres += 1; }

24 24 Test unitaire dune fonction void main() { // La fonction "syndicat". // Test positif. syndicat(); if (Nombre_de_membres_du_syndicat() == 0) cout << "Reussite.\n"; else cout << "echec\n"; // La fonction "Nombre_de_membres_du_syndicat". // Test positif. Inserer_nouveau_membre(6234, "Rioux", "Luc", "3456 rue Lavoie", 65000,1.50); Inserer_nouveau_membre(6500, "Roy", "Peter", "3429 rue du Lac", 35050,1.75); if (Nombre_de_membres_du_syndicat() == 2) cout << "Reussite.\n"; else cout << "echec\n";

25 25 Test unitaire dune fonction // La fonction "Calcule_cotisation_annuelle_moyenne". // Test positif. if (Calcule_cotisation_annuelle_moyenne() == 1.625f) cout << "Reussite.\n";else cout << "echec\n"; // Test négatif try { syndicat(); cout << Calcule_cotisation_annuelle_moyenne(); cout << "echec\n"; } catch(int i) { cout << i << " : succes\n"; }

26 26 Test unitaire dune fonction // La fonction "Inserer_nouveau_membre". // Test négatif. try { Inserer_nouveau_membre(5234, "Roy", "Luc", "3456 Lavoie", 65000, 1.50); cout << "echec\n"; } catch(int i) { cout << i << " : succes\n"; } try { Inserer_nouveau_membre(6234, "Roy", "Luc", "3456 Lavoie", 65000, 2.50); cout << "echec\n"; } catch(int i) { cout << i << " : succes\n"; }

27 27 Test unitaire dune fonction try { Inserer_nouveau_membre(6234, "Roy", "Luc", "3456 Lavoie", 65000, 1.50); cout << "echec\n"; } catch(int i) { cout << i << " : succes\n"; }

28 28 Test de la boîte blanche Il est impossible de garantir avec certitude quun programme fonctionne correctement dans tous les cas possibles. Le test unitaire comprend un nombre fini de tests. Tout test ne peut prouver que la présence de bogues, non leur absence. Dijkstra Il sagit ici de considérer le fonctionnement interne de la fonction en effectuant une trace de la fonction. La trace peut se faire via le débogueur ou à laide dopérations de sortie.

29 29 Test de la boîte blanche float RacineCarree(float a) { if (a < 0.0f) throw(1);// Lancement dune exception. if (a == 0.0f) return 0.0f; float xnouveau = a; float xvieux; do { xvieux = xnouveau; xnouveau = (xvieux + a / xvieux) / 2.0f; cout << " xvieux = " << xvieux << " xnouveau = " << xnouveau << endl; } while ((xnouveau - xvieux) >= EPSILON || (xnouveau - xvieux) <= - EPSILON ); return xnouveau; }


Télécharger ppt "Test et débogage Tests unitaires. Gestion derreurs. Notion détat, de pré-condition et de post-condition. Assertion. Traces de programme. Débogueur et ses."

Présentations similaires


Annonces Google