29
Test et débogage Tests unitaires. Gestion d’erreurs. 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.

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

Embed Size (px)

Citation preview

Page 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

Test et débogage

Tests unitaires. Gestion d’erreurs. 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.

Page 2: 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

2

La gestion des erreurs Plusieurs problèmes peuvent survenir pendant l’exécution d’un programme:

- insuffisance de mémoire, disque plein,- perte d’un fichier, imprimante non branchée ou à court de papier,- saisie non valide d’une valeur,- une fonction qui fonctionne mal,- etc.

Rôle des programmeurs face à ces erreurs d’exécution:- prévoir ces erreurs,- une application étant construite à partir d’un enchaînement d’appels de fonctions, toute fonction pouvant être confrontée à une erreur d’exécution, il devient nécessaire de pouvoir gérer les appels (les erreurs) en cascade de fonctions,- en informer l’utilisateur, 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.

Page 3: 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

3

Mise en œuvre de la gestion des erreurs

Ignorer l’erreur

Utiliser une variable globale qui sera initialisée par la fonctionprovoquant l’erreur d’exécution.Le contenu de cette variable sera ensuite récupéré et traité par la fonctionappelante de celle qui a entraîné l’erreur d’exécution.

Les variables globales n’offrent aucune garantie de sécurité. Cela signifie quel’on peut y accéder de n’importe où sans restriction. **** à éviter ****

PROBLÈME RENCONTRÉ : le code qui détecte l’erreur ne peut rienfaire pour sauver le travail de l’usager 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 l’erreur parce que l’opération ne peutêtre complétée. Ex. : lors d’un calcul.

Peut masquer des erreurs importantes.

Supposer que les erreurs ne se produiront pas est une mauvaise approche.

Page 4: 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

4

Mise en œuvre de la gestion des erreurs

Imprimer un message d’erreur 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 d’erreur dans levoisinage immédiat du code et déterminer si une vérificationadéquate des erreurs a été mise en place.

Désavantage: Le code est « pollué » par le traitement d’erreurs.Cela rend le code plus difficile à comprendre et à maintenir.

Envisageable au cours de la phase de mise au point seulement.**** à éviter ****

Page 5: 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

5

Mise en œuvre de la gestion des erreurs

Syntaxe : assert(expression logique);

Donne lieu à aucune action si l’expression 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 <stdio.h>#include <assert.h>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

Page 6: 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

6

Mise en œuvre de la gestion des erreurs

Abandonner Recommencer Ignorer

DébogageEnvoyer le

rapport d’erreurs Ne pas envoyer

Le programmeprend fin

Le programme prend finavec un message

qu’il termine anormalement.

Mettre fin à l’exé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éesde façon à ce qu’un programmeur puisse les détecter et les analyser.

Page 7: 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

7

Mise en œuvre de la gestion des erreurs

En C++, on utilise la librairie cassert.

#include <iostream.h>#include <cassert>

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";

}

Page 8: 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

8

Mise en œuvre de la gestion des erreurs

Approche classique d’interception d’erreurs: retour de code d’erreur

- 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 d’exécution.

Page 9: 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

9

Utilisation des valeurs de retour

#include <iostream.h>int Fonction_1(………….){

……...// Retourne 0 si aucune erreur n’est détectée;// un entier positif autrement.……..

}………...int Fonction_m(………….){

……...// Retourne 0 si aucune erreur n’est détectée;// un entier positif autrement.……..

}

Page 10: 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

10

Utilisation des valeurs de retour

int main(){

……….// Appel de la fonction Fonction_i.// Si une erreur est détectée,

alors gérer cette erreur en lien avec la fonction Fonction_isinon appel de la fonction Fonction_j

si une erreur est détectée,alors gérer cette erreur en lien avec Fonction_jsinon ………..

……….}

Cela met en évidence l’architecture nécessaire à des appels de fonctionsen cascade qui peuvent entraîner une erreur d’exécution.

Architecture peu lisible et surtout très pénible à maintenir(if-else imbriqué qui alourdit l’écriture et la mise à jour du programme).

Page 11: 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

11

Utilisation des valeurs de retour

#include <iostream.h>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 < 'A') | (c > 'Z'))

return 3;// Traitement.return 0;

}

Page 12: 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

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;

}elseif (Nom== Fn_B)

switch(code_erreur){

case 0 : cout << "Parfait"; return 0;case 3 : cout <<"Erreur 3"; return 1; // Erreur gravedefault: cout << "Erreur"; return 0;

}else cout << "incomplet "; return 0;

}

Page 13: 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

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;

}

Page 14: 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

14

Mécanisme des exceptions

Solution fiable et standardisée de gestion d’erreurs d’exécution qui ne sefonde pas sur les valeurs de retour des fonctions.

- permet de capturer toutes les exceptions d’un 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 » d’exécution d’un programme, cela permet d’améliorer la lisibilité des programmes et faciliter leur maintenance.

- permet de gérer nativement les appels de fonctions en cascade.

Page 15: 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

15

Test unitaire d’une fonction

Voici une fonction qui calcule la racine carrée d’un nombre entierpositif ou nul avec une précision EPSILON :

Exemple :

#include <iostream.h>

const float EPSILON = 0.00001f;

// 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 d’assembler chaque composante d’un programme en espérant quecela 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ésultatbooléen qui ne se préoccupe pas du fonctionnement interne de la fonction.

Page 16: 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

16

Test unitaire d’une fonction

float RacineCarree(float a){

if (a < 0.0f) throw(1); // Lancement d’une 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;}

Page 17: 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

17

Test unitaire d’une 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 && (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 && (z - x) >= -EPSILON) cout << "succes\n";

else cout << "echec\n";x = EPSILON;y = RacineCarree(x);z = y * y;if ((z - x) <= EPSILON && (z - x) >= -EPSILON) cout << "succes\n";

else cout << "echec\n";

Page 18: 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

18

Test unitaire d’une 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 d’une exception.{

cout << i << " : succes\n";}

}

Page 19: 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

19

Test unitaire d’une fonction

#include <iostream.h>#include <string.h>/* 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 dontle 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.*/

Page 20: 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

20

Test unitaire d’une 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 dusyndicat.

Valeur de renvoi :le nombre de membres du syndicat. */

{return Nombre_de_membres;

}

Page 21: 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

21

Test unitaire d’une 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;}

Page 22: 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

22

Test unitaire d’une 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. */

Page 23: 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

23

Test unitaire d’une 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 < 6000) | (numero_employe > 6999)) throw(4);

if((cotisation_annuelle < 0) | (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;

}

Page 24: 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

24

Test unitaire d’une 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";

Page 25: 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

25

Test unitaire d’une 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égatiftry{

syndicat();cout << Calcule_cotisation_annuelle_moyenne();cout << "echec\n";

}catch(int i){

cout << i << " : succes\n";}

Page 26: 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

26

Test unitaire d’une 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";}

Page 27: 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

27

Test unitaire d’une fonction

try{ Inserer_nouveau_membre(6234, "Roy", "Luc", "3456 Lavoie", 65000, 1.50); Inserer_nouveau_membre(6234, "Roy", "Luc", "3456 Lavoie", 65000, 1.50); cout << "echec\n";}catch(int i){

cout << i << " : succes\n";}

}

Page 28: 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

28

Test de la boîte blanche

Il est impossible de garantir avec certitude qu’un programme fonctionnecorrectement dans tous les cas possibles. Le test unitaire comprend unnombre fini de tests.

Tout test ne peut prouver que la présence de bogues, non leur absence.Dijkstra

Il s’agit ici de considérer le fonctionnement interne de la fonction en effectuantune trace de la fonction.

La trace peut se faire via le débogueur ou à l’aide d’opérations de sortie.

Page 29: 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

29

Test de la boîte blanche

float RacineCarree(float a){

if (a < 0.0f) throw(1); // Lancement d’une 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;}