3-real10-11 (1)
TRANSCRIPT
Annie Culet
OMGL2OMGL2
Conception Conception logiciellogiciel
EtapeEtape CodageCodage
IHM IHM
DonnDonnééeses
LogicielLogiciel
TestsTests
Etape 3Codage
� La phase de codage
Traduction de la conception dans un langage de programmation
Exécution des tests
� Résultat
Des sources commentés, commandes de compilation, exécution ..
Résultats des tests
La réalisation Annie Culet 3
Etape 3 : codage - démarche
Choix du langage de programmation et de la bibliothèque graphique :
Ada / GtkAda
Choix de la méthode de gestion de la persistance des données :
Outil de mapping typeAda/ relationnel : Mill
Choix du logiciel de test : framework AUnit
Réalisation :
L’IHM
Code des procédures "charge" et handler des modules IHM
Le noyau fonctionnel
Schéma relationnel des données
Code des procédures de la couche application du noyau fonctionnel
Tests fonctionnels unitaires
La réalisation Annie Culet 4
Bonnes pratiques de programmation
Il est indispensable de :
� comprendre son propre code
� en cours d'écriture
� après un mois (!)
� être compris par les autres développeurs
� changement d'équipe, maintenance, ...
Que fait ce programme ? Exemple tiré de http://www.labri.fr/~marlet
int a[1817];main(z,p,q,r){for
(p=80;q+p-80;p-=2*a[p])for(z=9;z--;)q=3&(r=time(0)+r*57)/
7,q=q?q-1?q-2?1-p%79?-1:0:p%79-
77?1:0:p<1659?79:0:p>158?-79:0,q?!a
[p+q*2]?a[p+=a[p+=q]=q]=q:0:0;for(;q++-1817;)printf(q%79?"%c":"%
c\n"," #"[!a[q-1]]);}
La réalisation Annie Culet 5
Bonnes pratiquesNommage : choisir des noms explicites, utliser des conventions de nommage
meilleure lisibilité du code,recherche facilitée, distingue l’élément désigné.
� Variables
nomAuteur, refOuvrage et non a, xentNomPays et non entry1, nom
� Types
tnomAuteur et non ch30
� Modules
p_ajouterOuvrage et non CU7
� Procédures, fonctions
consulterTousAuteurs (…) et non consult (…)
� Exceptions
ExPaysExistant et non e1
La réalisation Annie Culet 6
Bonnes pratiquesStructuration du code et homogénéité
modules utilitaires, procédures privées, …. (pas de code redondant)
homogénéité d’un style dans l’utilisation des structures de contrôles,
règles de style et d’indentation, … (cf. cours algo/prog)
Commentaires
Les commentaires doivent être ...
� nombreuxil n'y en a jamais assez
� uniquesduplication → incohérence, problème de maintenance
� pertinentspas de commentaire « affectation de la variable x à zéro » pour « x = 0; »
� au plus près de la chose commentéesystématiquement mis à jour
La réalisation Annie Culet 7
Bonnes pratiquesUtilisation d’un environnement de développement
� IDE environnement de développement intégré
� gestionnaire de versions
Exemples en Ada :
Gnat Programming Studio (Unix)
Eclipse (plug-in pour Ada),
AdaGide (Windows)
Réalisation IHM Annie Culet 8
Réalisation de l’IHM� Notion de programmation événementielle
� Présentation de la bibliothèque graphique GtkAda
� Codage de l’interface
� description des fenêtres en XML
� désignation des composants de l’IHM dans le code
� codage des procédures handler
� conversion de types
Réalisation IHM Annie Culet 9
Programmation événementielle
Application interactive
� Programmation événementielle : l’utilisateur a le contrôle
� une action de l'utilisateur sur un composant graphique déclenche un événement
� exemples d’événement : un clic sur un bouton de commande, une saisie dans une
zone de texte, une sélection dans une liste, un choix dans une case à cocher…
� les événements sont captés par le système d'exploitation puis pris en charge par la
boucle de gestion des événements
� si une procédure spécifique (handler, callback, procédure de rappel) est associée à
un événement, elle est alors exécutée
Exemple : le fait de cliquer sur le « Bouton A » va déclencher l'évènement « clicked »
associé à « Bouton A ». Le programmeur définit pour chacun des composants qu'il utilise,
les évènements qu’il veut gérer pour ce composant, et donc les procédures associées à la
gestion de cet évènement pour ce composant.
Réalisation IHM Annie Culet 10
Utilisation de la bibliothèque graphique GtkAda
� GtkAda : bibliothèque graphique
permet de réaliser des interfaces graphiques en Ada.
construite sur la bibliothèque graphique GTK+ (Gimp ToolKit, libre)
� GTK+ existe sur plusieurs plates-formes (UNIX, Windows)
� GTK+ est utilisable avec plusieurs langages de programmation
(C, C++, Ada, Perl, Python, PHP … )
� Intégrée avec un constructeur d’interface graphique Glade
� Glade sauvegarde un projet dans un fichier XML (.glade) contenant une description de
l'interface et les procédures à appeler pour traiter les différents signaux
� La bibliothèque libglade permet de créer et afficher dynamiquement les composants
à partir d’un fichier de définitions XML
� Glade.XML est le module de liaison à la bibliothèque libglade
Réalisation IHM Annie Culet 11
� XML : eXtensible Markup Language (Langage à balises extensible)
� Norme permettant de définir un format d'échange de données structurées selon les
besoins de l'utilisateur (standard du World Wide Web Consortium)
� Les données sont indépendantes de l'affichage.
� Un document XML est composé de balises et de texte libre.
� Un document XML peut se représenter sous la forme d'une arborescence d'éléments
<?xml version="1.0" encoding="ISO-8859-1"?><annuaire>
<personne><nom>Strummer</nom><prenom>Joe</prenom>
</personne><personne>
<nom>Cantat</nom><prenom>Bertrand</prenom>
</personne></annuaire>
XML
Réalisation IHM Annie Culet 12
<?xml version="1.0" encoding="UTF-8" standalone="no"?><glade-interface>
<widget class="GtkWindow" id="oneWindow" ><property name="visible">True</property><property name="title" translatable="yes">maFenetre </property><child>
<widget class="GtkVBox" id="vbox1"><child>
<widget class="GtkHBox" id="hbox1"><child>
<widget class="GtkLabel" id="label1" ><property name="label" translatable="yes">Nom de fam ille</property>
</widget></child>
<child><widget class="GtkEntry" id="entry_nomFamille" >
<property name="visible">True</property></widget>
</child></widget>
</child><child>
<widget class="GtkHBox" id="hbox2"><child>
<widget class="GtkButton" id="button_valider" ><property name="label" translatable="yes">Valider</ property><signal name="clicked" handler="on_button_valider_clicked" />
</widget>...
</child></widget>
</child></widget>
</child></widget>
</glade-interface>
balise et fermeture de balise
<mot clé …> </mot clé>
propriété : attribut = "valeur"class ���� type du composantid ���� identificateur du composanthandler ���� identificateur de la procédure
Extrait d’un fichier XML généré par Glade
Réalisation IHM Annie Culet 13
Description de l’interface� La description d’un composant graphique est introduite par la balise <widget
� Lorsqu’un composant en contient d’autres, ceux-ci sont placés dans un bloc <child
� Chaque composant graphique est défini par :
une classe (son type, par exemple GtkWindow, GtkButton, ...)
un id (son identificateur, qui permet d'y accéder par la suite)
<widget class="GtkEntry" id="entry_nomFamille">
un certain nombre de propriétés qui dépendent du type du composant
<property name="visible">True</property> indique que la fenêtre est visible
� La balise <signal introduit le type du signal émis qui déclenchera la procédure dont
l’identificateur est donné.
<signal name="clicked" handler="on_button_valider_clicked"/>
Réalisation IHM Annie Culet 14
usr/share/ada/adainclude/gtkada2/glade.XML.ads
-- This package is a binding to the libglade library that provides routines
-- to create widgets dynamically from an XML definition file.
with … ;with Gtk.Widget; use Gtk.Widget;
package Glade.XML is
type Glade_XML_Record is new Glib.Object.GObject_Record with private;type Glade_XML is access all Glade_XML_Record'Class;
procedure Gtk_New(XML : out Glade_XML;Fname : String;Root : String := "";Domain : String := "");
-- Create a new GladeXML object (and the corresponding widgets) from the XML file Fname.
-- Optionally it will only build the interface from the widget node Root (if it is not empty)
-- Domain, if not null, is the international domain to use for string translation.
Réalisation IHM Annie Culet 15
usr/share/ada/adainclude/gtkada2/glade.XML.ads
procedure Signal_Connect(XML : access Glade_XML_Record;Handlername : String;Func : System.Address;User_Data : System.Address);
function Get_Widget
(XML : access Glade_XML_Record; Name : String) return Gtk_Widget;
-- This function is used to get the Gtk_Widget corresponding to name in the interface description.
-- You would use this if you have to do anything to the widget after loading.
function Get_Widget_Name
(Widget : access Gtk_Widget_Record'Class) return String;
….
Réalisation IHM Annie Culet 16
Programme principal "type"
with Gtk.Main; with Glade.XML;
procedure start is
xml : Glade_XML ; -- variable de type Glade_XML
begin
-- Initialisation de variables d’environnement
Gtk.Main.Set_Locale;-- Initialisation de Gtk Gtk.Main.Init; --Chargement de la description complète de l’interface à partir du fichier XML et affichage
Glade.XML.Gtk_New (xml, " monFichier.glade");-- Lancement de la boucle évènementielle de Gtk
Gtk.Main.Main;
end start;
Réalisation IHM Annie Culet 17
"Chargement" d’une description de fenêtre
� "Chargement" de la description d’une fenêtre à partir de son id dans le fichier XML
xml : Glade_XML ; -- variable de type Glade_XML
Glade.XML.Gtk_New (xml, "fichier.glade", "oneWindow") ;<widget class="GtkWindow" id="oneWindow">
…
</widget>
<widget class="GtkWindow" id=“anOtherWindow">
…
</widget>
� " Récupération" d’un composant de la fenêtre à partir de son id dans le fichier XML avec
conversion de type
entNom : Gtk_Gentry; -- variable de type Gtk_Gentry
entNom := Gtk_Entry (Glade.XML.Get_Widget (xml, "entry_nomFamille");
<widget class="GtkEntry" id="entry_nomFamille">
paramètre en sortie
paramètre en entrée
Réalisation IHM Annie Culet 18
Association d’une procédure Handler
� Association d’une procédure Handler désignée par son identificateur dans la description de l’interface à une procédure dans un programme :
Glade.XML.signal_connect (xml, "on_button_valider_clicked", procTraite’address,
System. null_address);
<signal name="clicked" handler="on_button_valider_clicked"/>
procedure procTraite (widget : access Gtk_Widget_Record’Class) isbegin-- traitement à effectuer sur émission du signal
end procTraite;
Réalisation IHM Annie Culet 19
with Glib.Object;with Glib.Properties;with Glib.GSlist;with Gtkada.Types;
package Gtk.Object is
type Gtk_Object_Record is new Glib.Object.GObject_Record with private;type Gtk_Object is access all Gtk_Object_Record'Class;
procedure Destroy (Object : access Gtk_Object_Record);
-- Destroy the object.
-- The object is then unref-ed, and the memory associated with the object is freed.
-- In most cases, only toplevel widgets (windows) require explicit destruction
-- because when you destroy a toplevel its children will be destroyed as well.
usr/share/ada/adainclude/gtkada2/Gtk.Object.ads
Réalisation IHM Annie Culet 20
usr/share/ada/adainclude/gtkada2/Gtk.Widget.ads….
with Gtk.Enums;with Gtk.Object;
with Gtk.Style;
package Gtk.Widget istype Gtk_Widget_Record is new Object.Gtk_Object_Record with private;type Gtk_Widget is access all Gtk_Widget_Record'Class;
procedure Show (Widget : access Gtk_Widget_Record);-- Schedule the widget to be displayed on the screen when its parent is also shown
procedure Hide (Widget : access Gtk_Widget_Record);-- Hide the widget from the screen
-- If Widget was visible, it is immediately hidden.
procedure Show_All (Widget : access Gtk_Widget_Record);-- Show Widget and all its children recursively.
procedure Hide_All (Widget : access Gtk_Widget_Record);-- Hide Widget and all its children.
procedure Set_Sensitive (Widget : access Gtk_Widget_Record; Sensitive : Boolean := True);-- Modify the sensitivity of the widget.
-- An insensitive widget is generally grayed out, and can not be activated.
Réalisation IHM Annie Culet 21
with Gtk.Widget;
package Gtk.Editable is
type Gtk_Editable_Record is new Gtk.Widget.Gtk_Widget_Record with private;type Gtk_Editable is access all Gtk_Editable_Record'Class;
-- Gtk_Editable is now an interface, not an object
procedure Insert_Text (Editable : access Gtk_Editable_Record; New_Text : UTF8_String; Position : in out Gint);-- Insert the given string at the given position.
-- Position is set to the new cursor position. If Position is -1, the text is appended at the end.
procedure Delete_Text (Editable : access Gtk_Editable_Record; Start_Pos : Gint := 0; End_Pos : Gint := -1);-- Delete the characters from Start_Pos to End_Pos.
-- If End_Pos is negative, the characters are deleted from Start_Pos to the end of the text.
procedure Set_Editable (Widget : access Gtk_Editable_Record; Editable : Boolean := True);
usr/share/ada/adainclude/gtkada2/Gtk.Editable.ads
Réalisation IHM Annie Culet 22
with Glib.Properties;with Gtk.Editable;
with Gtk.Entry_Completion; use Gtk.Entry_Completion;with Pango.Layout;
package Gtk.GEntry is
type Gtk_Entry_Record is new Gtk.Editable.Gtk_Editable_Record with private;-- Gtk_Entry is actually a child of Gtk_Widget, and implements the
-- Gtk_Editable interface, but GtkAda does not support yet interfaces,
type Gtk_Entry is access all Gtk_Entry_Record'Class;subtype Gtk_GEntry is Gtk_Entry;
procedure Set_Text (The_Entry : access Gtk_Entry_Record; Text : UTF8_String);
function Get_Text (The_Entry : access Gtk_Entry_Record) return UTF8_String;
-- Modify the text in the entry.
-- The text is cut at the maximum length that was set when the entry was created.
-- The text replaces the current contents.
procedure Set_Max_Length (The_Entry : access Gtk_Entry_Record; Max : Gint);
-- Set the maximum length for the text. The current text is truncated if needed.
usr/share/ada/adainclude/gtkada2/Gtk.Gentry.ads
Conception Logiciel Annie Culet 23
Architecture d’un logiciel interactif
Adaptateur des données
Ajuste les différences de modélisation entre les données de la présentation et celles du noyau
fonctionnel (conversion de type, interprétation de cases à cocher, boutons radio, …)
Couche IHM
Noyau Fonctionnel
acquisition des données en provenance de l’utilisateur
restitution de données vers l’utilisateur
appel des procédures du noyau fonctionnel
Présentation et dialogue
Adaptateur des données
récupération des résultatsdonnées converties aux types de données du langage
données au format des composants de la fenêtre
données au format des types de données du langage
données converties au format des composants de la fenêtre
La réalisation Annie Culet 24
Adaptateur de données� Module de conversion de type : p_conversion (fourni localement)
� une fonction : empty
-- teste si une chaîne UTF8 est vide ou non
� des procédures : to_ada_type
-- conversion d’une chaîne UTF8 vers un type Ada
� des fonctions : to_string
-- conversion un type numérique non contraint et date en chaîne UTF8
+ modules génériques pour les conversions concernant
les numériques contraints et les énumérés
avec des procédures/fonctions : to_ada_type et to_string
� une procédure to_ada_type :
� lève l’exception ExConversion si le type en sortie n’est pas conforme au type Ada attendu,
� affiche un message d’erreur dans une boîte de dialogue.
Réalisation IHM Annie Culet 25
Module de conversion de typepackage p_Conversion is
-- teste si une chaîne est vide ou non
function empty (entree : UTF8_String) return boolean;
-- effectue une conversion d’une chaîne vers un type Ada
(chaîne ou numérique non contraint)
-- si le type en sortie n’est pas conforme au type Ada attendu, affiche un message d’erreur et lève
l’exception ExConversion
procedure to_ada_type (entree : UTF8_String; sortie : out integer) ;
procedure to_ada_type (entree : UTF8_String; sortie : out float) ;
procedure to_ada_type (entree : UTF8_String; sortie : out string );
ExConversion : exception ;
-- convertit un type numérique non contraint en chaîne
function to_string (item : float) return string;
function to_string (item : integer) return string;
Réalisation IHM Annie Culet 26
Module de conversion de type
-- modules génériques pour les conversions concernant les numériques contraints et les énumérés
-- si le type en sortie des procédures to_ada_type n’est pas conforme au type Ada attendu, affiche
un message d’erreur et lève l’exception ExConversion
generic
type entier is range <> ;
package P_Entier is
procedure to_ada_type (entree : UTF8_String; sortie : out entier );
ExConversion : exception ;
function to_string (item : entier) return string;
end P_Entier;
Réalisation IHM Annie Culet 27
Module de conversion de typegeneric
type reel is digits <> ;
package P_Reel is
procedure to_ada_type (entree : UTF8_String; sortie : out reel );
ExConversion : exception ;
function to_string (item : reel) return string;
end P_Reel;
---------------------------------------------------------------------------------------------------------------------------
generic
type t_enum is (<>);
package P_Enum is
procedure to_ada_type (entree : UTF8_String; sortie : out t_enum );
ExConversion : exception ;
function to_string (item : t_enum) return string;
end P_Enum;
Conception Logiciel Annie Culet 28
Contrôles réalisés dans l’IHM
� A l’issue d’une saisie, les contrôles de type et de présence de données obligatoires
sont effectués dans la couche IHM avant tout appel d’une procédure du NF.
Les types des paramètres d’une procédure du NF sont donc vérifiés avant son appel.
� Contrôle de présence pour une donnée obligatoire
Ex. vérification qu’une zone d’entrée n’est pas vide
� Contrôle du type d’une donnée saisie
si l’adaptateur n’arrive pas à convertir le contenu d’une zone d’entrée, un
élément d’une liste ou d’une table au type de la donnée correspondant dans le
langage de programmation (chaîne d’une longueur donnée, entier, réel,
énuméré, …), il y a une erreur de type.
Réalisation IHM Annie Culet 29
Codage de l’interfaceModule fenêtre
� Choisir la modalité de la fenêtre� Définir une variable par composant manipulé
� Lorsqu’une fenêtre est composée de plusieurs régions, pour focaliser le dialogue dans une région, les autres régions doivent être rendues inaccessibles
Set_Sensitive (butEnregistrer, false)
Set_Editable (entNom, false)
Protection contre les erreurs
Réalisation IHM Annie Culet 30
Code des procédures handler
charge (…)enregistrerAdherent()…
p_window_enregAdherentConversion Chaînes /types du langageException
ExConversion
p_conversionCouche IHM
Noyau FonctionnelCouche application
bibliothèque graphiqueGtkAda
creerAdherent ( adh : tAdhérent) ;
p_application
utilise
légendepropage exception
Interface
creerAdhérent (d adh : tAdhérent) ;
{ } �{ le numéro de l’adhérent est généré. L’adhérent adh est créé sans emprunt}
Enregistrer/générer numAdhérent
et créer adhérent
Enregistrer un adhérent
Annuler
OkB. Confirm
F. EnregAdh
Interface p_window_enregAdherent
charge (…)enregistrerAdherent(…){ } �{la boîte de dialogue de confirmation de la création de l’adhérent est affichée; la fenêtre “EnregAdh“ est fermée }annulerOperation(..) { } �{la fenêtre “EnregAdh“ est fermée}
Réalisation IHM Annie Culet 31
Code des procédures handler
procedure enregistreAdherent (…) is
1. – vérif. présence infos obligatoires, récupération des valeurs saisies,conversion vers types Ada du modèle de données
adh : tAdherent;
if empty (get_text(entNom)) then rep := Message_Dialog (« le nom est obligatoire »); else ….p_conversion.to_ada_type(get_text(entNom), adh.nomAdherent)); ….if get_active(radiobutAdulte) then adhVal.categorie := adulte; ….
2. -- appel de la procédure du noyau fonctionnel. Le type adhVal est correct. Message de confirmation.
creerAdherent (adhVal , adhIdf) ;rep := Message_Dialog (« Confirmation enregistrement adhérent » & adhVal.numAdherent);destroy (FenEnregAdherent); -- destruction de la fenêtre
3. -- traitement des exceptions
when ExConversion => return;when ExopFatal => rep := Message_Dialog (« Problème sur la base »);
Réalisation IHM Annie Culet 32
Code des procédures handler
charge (…)rechercherAdherent()modidierAdherent()
p_FenModifCoordAdherent
Couche IHM
Noyau FonctionnelCouche application
Modifier coordonnées adhérent
Annuler
Rechercher
/ consulter adhérent
Enregistrer / modifier adhérent
Annuler
Ok
[non trouvé]Ok
F. ModifAdhrégion 1
F. ModifAdhrégion 2
B. Confirm
B.Erreur
[trouvé]
Interface
consulterAdhérent (d n : tnumAdhérent; r adh : tAdhérent);
{ } �{l’adhérent n est consulté et les informations relatives à l’adhérent sont retournées dans adh}Exception : AdhérentNonTrouvé {l’adhérent n n’existe pas}
modifierAdhérent (d n : tnumAdhérent; d adh : tAdhérent);
{ } �{les coordonnées de l’adhérent n sont modifiées et sont enregistrées}
retourne résultat
utilise
légende
propage exception
consulterAdherent (n : …) : tAdherent;Exception ExAdherentNonTrouve
modifierAdherent (adh : tAdherent)
p_application
Réalisation IHM Annie Culet 33
Code des procédures handler
Procedure on_buttonRechercher_clicked (…) is
1. -- vérif. présence du numéro, récupération de la valeur saisie et conversion au type Ada du modèle de données
-- les boutons Enregistrer/Annuler ne doivent pas être activables, ….
2. -- appel de la procédure du noyau fonctionnel. Le type adhIdf est correct.
consulterAdherent (adhIdf, adhVal ) ;
3. -– affichage des valeurs résultat dans la région 2 de la fenêtre-- les boutons Rechercher/Annuler ne doivent pas être activables, …. Le nom est non modifiable.
set_text (entNom, adhVal.nomAdherent); -- pas de conversion puisque le nom est une chaîne
4. -- traitement des exceptions
when ExConversion => return; when ExAdherentNonTrouve => rep := Message_Dialog (« Adhérent non trouvé »); when ExopFatal => rep := Message_Dialog (« Problème sur la base »);
Spécification{ } �{les informations relatives à l’adhérent sont affichées dans la fenêtre}
Réalisation IHM Annie Culet 34
Code des procédures handler
Procedure on_buttonEnregistrer_clicked (…) is
1. -- récupération de la nouvelle adresse saisie et conversion au type Ada du modèle de données
2. -- appel de la procédure du noyau fonctionnel. Message de confirmation. Destruction de la fenêtre
modifierAdherent (adhIdf, adhVal ) ; ……
3. -- traitement des exceptions
when ExConversion => return; when ExopFatal => rep := Message_Dialog (« Problème sur la base »);
Spécification{ } �{les nouvelles informations relatives à l’adhérent sont enregistrées}
Réalisation IHM Annie Culet 35
Code des procédures handlerInterface couche ApplicationconsulterOuvrage(ouvIdf : in tOuvrageIdf ;
ouvVal : out tOuvrageVal ; autVal : out tautVal);Exception : ExOuvrageNonTrouveException : ExOuvrageDisponible
consulterEmprunt (empIdf : in tEmpruntIdf ;empVal : out tEmpruntVal);
type tOuvrageVal is recordréfOuvrage : trefOuvrage;…auteur : tAuteurIdf;empruntOuvrage : tEmpruntIdf := empruntNul;
end record;
Procedure on_buttonRechercher_clicked (…) is
1. -- récupération de la valeur saisie dans la fenêtre et conversion
2. -- appel au noyau fonctionnel.
consulterOuvrage (ouvIdf, ouvVal , autVal) ;consulterEmprunt (ouvVal.empruntOuvrage, empVal);
3. -– affichage des valeurs résultat affiche_InfosOuvrageAuteur; -- procédure privéeaffiche_InfosEmprunt; -- procédure privée
4. -- traitement des exceptions
when ExOuvrageNonTrouve => rep := Message_Dialog (« Ouvrage non trouvé »);when ExOuvrageDisponible => affiche_InfosOuvrageAuteur;
set_text (entDateEmprunt, « Ouvrage disponible » ); -- + les autres
Spécification{ } �{les infos sur l’ouvrage, son auteur et éventuellement son emprunt sont affichées dans la fenêtre}
La réalisation Annie Culet 36
Conception des données� Solution retenue :
� les données sont stockées dans une base de données relationnelle
� Dans ce contexte : détermination du schéma relationnel de données
On applique les « règles » de traduction Diagrammes de classes / Schéma relationnel
Le schéma relationnel obtenu doit préciser :
� les clés primaires
� les clés étrangères
Il faut également préciser :
� le caractère « obligatoire » ou non des attributs
� toutes les contraintes d’intégrité du diagramme de classes non prises en compte
par le schéma relationnel
Conception Données Annie Culet 37
à partir d’un diagramme de classes …
réfOuvrage : ChainetitreOuvrage : ChainegenreOuvrage : Enum {roman, policier, SF, documentaire, théâtre, poésie, BD }dateParution : Date
Ouvrage
nomAuteur : ChaineprénomAuteur : ChainenationalitéAuteur : Chaine
Auteur
auteur
publication 1..1
1..*
numAdhérent : ChainenomAdhérent : ChaineadresseAdhérent : Chainecatégorie : Enum {jeune, adulte}
Adhérent
dateEmprunt :Date
Emprunt0..4
0..1
emprunt
emprunteur
{taille(numAdhérent) ≤ 5 et unique}
{concat(nomAuteur, prénomAuteur) unique}
{taille(réfOuvrage) ≤ 6 et unique} Gestion d’une bibliothèque
La réalisation Annie Culet 38
Passage du diagramme de classes au relationnel
� R0 : Chaque classe C devient une relation RC dont :
les attributs sont les attributs monovalués de la classe C,
la clé de RC est l’attribut (ou groupe d’attributs) marqué par la contrainte {unique} ou un nouvel attribut.
nomAuteurprénomAuteurnationalitéAuteur
Auteur
{concat(nomAuteur, prénomAuteur) unique}
AUTEUR(nomAuteur, prénomAuteur, nationalité)
minutesRetardraison
Retard RETARD(cléRetard, minutesRetard, raison)
La réalisation Annie Culet 39
Passage du diagramme de classes au relationnel
� R1 : un attribut multivalué d’une classe C devient une relation dont :
les attributs sont l’attribut multivalué de la classe C et la clé de la relation RC représentant la classe C qui est clé étrangère,
la clé de cette relation est l’ensemble des 2 attributs.
Personne
numSécu {unique}nomtelPersonnel [1..2]
PERSONNE(numSécu, nom)TELPERSONNE(#numSécu, telPersonnel)
La réalisation Annie Culet 40
Passage du diagramme de classes au relationnel
� R2 : une association dont l’un des rôles est de multiplicité 1 détermine une clé étrangère dans la relation RC représentant la classe C associée au rôle inverse.
Les attributs de la classe association deviennent attributs de cette relation RC.
nInsee {unique}nomage
Personne
nomService {unique}batiment
Service
service
employé 1
1..*
PERSONNE(nInsee, nom, age, # nomService, dateEmbauche)SERVICE(nomService, batiment)
dateEmbauche
Embauche
La réalisation Annie Culet 41
Passage du diagramme de classes au relationnel
� R3 : une association dont l’un des rôles est de multiplicité 0..1 devient une relation dont :
les attributs sont les clés des relations représentant les classes associées et les attributs de la classe association,
la clé est la clé de la relation RC représentant la classe C associée au rôle inverse ,
les clés des relations représentant les classes associées sont clés étrangères.
réfOuvrage {unique}titreOuvragegenreOuvrage
Ouvrage
numAdhérent {unique}nomAdhérentadresseAdhérent
Adhérent0..4
0..1emprunt
emprunteur
dateEmprunt
Emprunt
OUVRAGE(réfOuvrage, titreOuvrage, genreOuvrage)ADHERENT(numAdhérent, nomAdhérent,adresseAdhérent)EMPRUNT(#réfOuvrage, #numAdhérent, dateEmprunt)
La réalisation Annie Culet 42
Passage du diagramme de classes au relationnel
� R4 : une association dont les rôles sont de multiplicité * devient une relation dont :
les attributs sont les clés des relations représentant les classes associées et les attributs de la classe association,
la clé est formée par les clés des relations représentant les classes associées, qui sont aussi clés étrangères.
nom {unique}spécialité
Enseignant
intitulé {unique}matièrenbHeures
Cours
coursDonné
1..*enseignant
1..*
ENSEIGNANT(nom, spécialité)COURS(intitulé, matière,nbHeures)ENSEIGNE(#nom, #intitulé, numSalle)
numSalle
Salle
La réalisation Annie Culet 43
Passage du diagramme de classes au relationnel
� Que devient un attribut calculé d’une classe ?
� en général non représenté : il sera calculé dynamiquement (par une procédure au
niveau application).
� on peut néanmoins décider (pour des raisons de performance) de représenter
l'attribut dérivé mais il sera nécessaire dans ce cas d’assurer que la valeur stockée
évolue en même temps que les attributs sur lesquels le calcul dérivé porte.
� Compléter votre schéma en notant les attributs obligatoires avec *
� Exprimer toutes les contraintes d’intégrité en français
La réalisation Annie Culet 44
… le schéma relationnel
OUVRAGE (réfOuvrage, titreOuvrage*, genreOuvrage*, dateParution*, #(nomAuteur, prenomAuteur)*)
AUTEUR (nomAuteur, prenomAuteur, nationalité*)
ADHERENT (numAdhérent, nomAdhérent*, adresseAdhérent*, categorie*)
EMPRUNT (#réfOuvrage, #numAdhérent*, dateEmprunt*)
contrainte d’intégrité :
Dans EMPRUNT: un adhérent a au plus 4 ouvrages empruntés.
Gestion d’une bibliothèque
La réalisation Annie Culet 45
Mapping vers le relationnel depuis Ada
� Motivation
avoir un niveau d'accès aux données qui masque la couche SGBD relationnel qui réalise la persistance
=>permet de développer une couche application sans SQL
� Une variable structurée permet de représenter directement un n-uplet (une entité) stocké dans une table du SGBD
Le type structuré a des attributs qui correspondront aux champs (colonnes) de la table qu'elle représente
On définit un type structuré pour chaque table de la BD
La réalisation Annie Culet 46
Mapping vers le relationnel depuis Ada
Andrea
Andrea
prenomAuteur
Camilleri
Camilleri
nomAuteur
février 2006romanla prise de MalakéCAM128
janvier 2008policierla lune de papierCAM645
dateParutiongenreOuvragetitreOuvragerefOuvrage
� On définit un type structuré pour représenter un enregistrement de cette table :
type tOuvrage is recordrefOuvrage : String; titreOuvrage : String; genreOuvrage : tgenre_enum;dateParution: Date;nomAuteur : String;prenomAuteur : String;
end record;
� On définit également des procédures/fonctions pour manipuler les variables de ce type.
Exemple : table Ouvrage contenant des ouvrages :
La réalisation Annie Culet 47
Besoin d’un outil
� Implémenter "à la main" un mapping est une tâche répétitive, fastidieuse et délicate (en particulier adéquation entre les types de données SQL et ceux de Ada)
� Il existe des outils dans d’autres langages pour réaliser cette corvée de façon "propre" et automatique :
en java : Torque, ...
en PHP : Propel,...
� En Ada, un outil a été développé selon les mêmes principes : Mill
http://www.virtual-worlds.biz/downloads/mill/
La réalisation Annie Culet 48
Principe de Mill (de Torque, …)
Fichier décrivant le
schéma relationnel
de la BD
(format XML)
Un type structuré par table
Un ensemble de procédures par table
GénérateurMill
La réalisation Annie Culet 49
Schéma XML correspondant au schéma relationnel de la BD
<database name= "Biblio" >
<table name= "Ouvrage" >
<column name= "refOuvrage" primaryKey="true" type="CHAR" size=" 6"/>
<column name= "titreOuvrage" required="true" type="CHAR" size=" 80"/>
<column name="genreOuvrage" required="true" type="ENUM"
values = "roman policier SF documentaire théâtre poésie BD" />
<column name="dateParution" required="true" type=" DATE" />
<column name= "nomAuteur" required="true" type="CHAR" size=" 30"/>
<column name= "prenomAuteur" required="true" type="CHAR" size=" 30"/>
<foreign-key foreignTable="Auteur" >
<reference foreign="nomAuteur" local= "nomAuteur "/>
</foreign-key>
<foreign-key foreignTable="Auteur" >
<reference foreign="prenomAuteur" local= "prenomAuteur"/>
</foreign-key>
</table>
Réalisation Logiciel Annie Culet 50
Codage du logiciel
� Codage des modules de la couche application du noyau fonctionnel
� Génération de la couche utilitaire avec Mill
� Utilisation des modules fournis par la couche utilitaire
� Propagation des exceptions levées dans la couche application vers la couche IHM
� Codage des tests fonctionnels unitaires
� Écrire les "test_case" avec AUnit et les exécuter
Réalisation Logiciel Annie Culet 51
Architecture en couches du NF
ConsulterOuvrage(…)ConsulterAuteur(…)ModifierAdhérent(…)Exception
…
p_application
couche Application
couche Utilitaire ouvrage_io auteur_io emprunt_io
couche Noyau
Types des donnéesde l’application
biblio_data
gnade/postgres
modules réalisant le mapping vers le relationnel
modules réalisant les CU de l’application et les primitives sur la base
save ()retrieve () …
save ()retrieve () …
save ()retrieve () …
…
La réalisation Annie Culet 52
Utilisation de Mill
� Pour une BD biblio contenant les tables XXX, Mill génère :
� un module biblio.sql qui permet de créer les relations de la base (CREATE TABLE ouvrage …)
� un module biblio_data dans lequel sont déclarés pour chaque table XXX :
le type structuré XXX correspondant ( type touvrage is record ….)
un module XXX_list qui gère un ensemble d’éléments de ce type ( package ouvrage_list …)
� un module base_types qui contient en particulier les déclarations des types énumérésutilisés s’il y en a (type tgenre_enum, …)
� pour chaque table XXX un module XXX_io qui contient les procédures permettant de réaliser les mises à jour dans la table (ouvrage_io, …)
Retrieve_By_PK, Retrieve, Save, Delete
Add_nomAtt pour valoriser une clause WHERE (add_titreOuvrage, …)
La réalisation Annie Culet 53
Création simple
� Pour créer un nuplet dans une table, il suffit de donner des valeurs aux attributs de la variable structurée correspondante et de la sauvegarder dans la BD par la procédure save.
� Mill traduira cela par une requête SQL « insert ».
ouvrage_item : biblio_data.touvrage;
ouvrage_item.refOuvrage := "CAM645" ;
ouvrage_item.titreOuvrage := "la lune de papier" ;
….
ouvrage_io.save (ouvrage_item, false);
une exception est levée si l’ouvrage existe déjà.
La réalisation Annie Culet 54
Rechercher un nuplet dans la base� Pour rechercher à partir de la clé primaire, il suffit de passer la valeur de la clé à la
procédure retrieve_by_pk.
� Mill va exécuter une requête " select "
ouvrage_item : biblio_data.touvrage; ref : Unbounded_String;
ref := "CAM645" ;ouvrage_item := ouvrage_io.retrieve_by_pk (ref);
retourne biblio_data.null_ouvrage s’il n’est pas trouvé
à tester avec la fonction Is_Null
La réalisation Annie Culet 55
Rechercher un ensemble de nuplets dans la base
� Pour rechercher à partir de la valeur d’un ou plusieurs attributs, on affecte les attributs et on utilise la procédure retrieve.
� Mill va exécuter une requête " select " avec une clause WHERE
Vector est une collection d’éléments (voir les containers en Ada)ouvrage_list_item: biblio_data.ouvrage_list.vector; criteria : db_commons.criteria;
rechercher tous les ouvrages
ouvrage_list_item := retrieve(criteria)
rechercher tous les ouvrages du genre policier
ouvrage_io.add_genreOuvrage (criteria, policier);ouvrage_list_item := retrieve(criteria);
retourne biblio_data.null_ouvrage_list si le résultat est vide
à tester avec la fonction Is_Empty
La réalisation Annie Culet 56
Modification, suppression� Pour modifier un nuplet
idem création mais ….ouvrage_io.save (ouvrage_item, true);
� Pour supprimer un nupletidem rechercher mais ….ouvrage_io.delete (criteria)
La réalisation Annie Culet 57
Equivalence des types Ada/SQL
values
size, scale
size
Attributs
type énuméré
type décimal contraint
boolean
long_float
integer
unbounded_string
ada.calendar.time
Type ada
ENUM
DECIMAL
BOOLEAN
REAL
INTEGER
CHAR
DATE|DATETIME|TIME
Type Fichier XML
La réalisation Annie Culet 58
ODBC, GNADE� ODBC (Microsoft) (comme JDBC Sun Java) :
API Interface de Programmation (Application Programming Interface) permet la connexion, interrogation et sa mise à jour de la base de données, traitement des erreursindépendante de tout SGBD particulier nécessite un driver (pilote) spécifique au SGBD utilisé
� GNADE (GNU Ada Database Environment) : interface SQL avec Ada 95 suivant le principe de l'Embedded SQL (SQL embarqué).
Utilise l’API ODBC disponible sur Linux, Solaris et Windows/NT
Réalisation Logiciel Annie Culet 59
Traitement des exceptions
….créerOuvrage(…)Exceptionwhen
ExOuvrageExistant => …
ExOuvrageExistantcréerOuvrage(…)
ExOuvrageAjouterajouter (…)
ExFiEcrireecrire(…)
interfacecorps
coucheApplication
coucheUtilitaire
coucheFichier
couche IHM Module
Toutes les exceptions sont propagées jusqu’au niveau IHM pour y être traitées dans leur contexte.
noyau fonctionnel
Propagation exception
Réalisation Logiciel Annie Culet 60
Exception - Rappel1. traitée localement
Procedure Q(…)begin… ;P(…);… ;
end Q;
Procedure P(…)begin… ;�_____… ;Exceptionwhen Ex => … ;
end P;
TE
si Ex est levée, elle est traitée par le Traite-Exception TE qui achève l’exécution de P. Q ne sait pas que son appel à P s’est "mal terminé".
l’exception Ex est levéeabandon de la suite d’instructions
contrôle transféré au Traite-Exception TE
Réalisation Logiciel Annie Culet 61
Exception - Rappel 2. propagée à l’unité appelante
Procedure Q(…)begin… ;P(…);�_____… ;
Exceptionwhen Ex => … ;
end Q;
Procedure P(…)begin… ;�_____… ;end P;
TE
la même exception Ex (ou ExAlias si renommage) est levée (propagation) et traitée par le Traite-Exception TE qui achève l’exécution de Q
l’exception Ex est levéeabandon de la suite d’instructions
La propagation continue tant que l’unité qui la reçoit n’a pas de Traite-Exception.Si la propagation arrive au programme principal, l’exécution du programme s’arrête.
l’exception peut être propagée sous un autre nomExAlias : exception renames Ex
pas de Traite-Exception dans P � propagation dans l’unité appelante
Réalisation Logiciel Annie Culet 62
Exception - Rappel 3. redéclenchement d’une exception
Procedure Q(…)begin… ;P(…);�_____… ;
end Q ;
Procedure P(…)begin… ;�_____… ;Exceptionwhen Ex => … ;
raise Ex2;end P ;
TE
l’exception Ex2 est levée
l’exception Ex est levéecontrôle transféré au Traite-Exception TE
redéclenche l’exception Ex2 pour l’unité appelante
Ex2 est traitée par un Traite-Exceptionou propagée à l’unité appelante
Redéclencher une autre exception est une forme de propagation de l’exception initiale.
Réalisation Logiciel Annie Culet 63
Renommaged’une exception, d’une procédure
�Utilisation des ressources d’un module :
pour établir la visibilité de son interface : clause with
�Identification d’une ressource (procédure, fonction, exception, …)
� par le nom complet (notation pointée) : préfixage par le nom du module
P_ ensembleOuvrage.ExEnsConsulter
� visibilité directe : utilisation de la clause use mais problème de conflit de noms
ExEnsConsulter de p_ensembleOuvrage ou de p_ensembleAuteur ?
� renommage (désignation sous un nouveau nom) : utilisation du renames
ExOuvrageConsulter : exception renames p_ensembleOuvrage.ExEnsConsulter;
les exceptions de la couche application sont souvent redéclenchées pour être propagées sous le nom énoncé dans la conception de l’interface de cette couche.
Réalisation Logiciel Annie Culet 64
Exemple de propagation d’exceptionsProcedure consulterOuvrage (oIdf : in touvrageIdf ; oVal : out tOuvrageVal ; aVal : out tauteurVal);
…ensembleOuvrage.consulter (oIdf, oVal);-- cas ouvrage non empruntéif oVal.emprunt = empruntNul
then raise ExOuvrageDisponible;end if;ensembleAuteur.consulter (oVal.auteur, aVal);
exceptionwhen ExOuvrageConsulter => raise ExOuvrageNonTrouvewhen ExOuvrageFatal | ExAuteurFatal => raise ExOpFatal;
end consulterOuvrage;
Procedure on_button_rechercher_clicked (…) is…consulterOuvrage (oIdf , oVal , aVal );consulterEmprunt (oVal.emprunt , eVal);…exception when ExOuvrageNonTrouve => rep := Message_Dialog (« Référence de l’ouvrage incorrecte»);when ExopFatal => rep := Message_Dialog (« Problème sur la base »);when ExOuvrageDisponible => …when ExConversion => return; couche IHM
couche Application
Réalisation IHM Annie Culet 65
Utilitaire pour le composant Tree_View
with Gtk.Tree_View; use Gtk.Tree_View;
with Gtk.Tree_Store; use Gtk.Tree_Store;
with Glib; use Glib;
-- prévu uniquement pour afficher des chaînes
package p_util_treeview is
-- création d'une colonne dans une tree_view avec un titre. Le titre peut être visible ou non.
procedure creerColonne (titre : UTF8_String ; treeview : Gtk_Tree_View; titreVisible : boolean);
-- création d'un modèle pour une tree_view. Il contient les données destinées à alimenter la vue.
procedure creerModele ( treeview : Gtk_Tree_View; modele : out Gtk_Tree_Store);
end p_util_treeview;
Réalisation IHM Annie Culet 66
Affichage dans un composant Tree_View
Interface couche ApplicationconsulterAuteur(autIdf :in tAuteurIdf ; listeOuvVal :out tlisteOuvrageVal);
Exception : ExAuteurNonTrouvé
type tAuteurVal is recordnomAuteur : tnomAuteur ;prénomAuteur : tprénomAuteur ;nationalitéAuteur : tnationalitéAuteur ;publication : tEnsOuvrageIdf;
end record;
Procedure on_buttonRechercher_clicked (…) is
1. -- récupération de la valeur saisie et conversion au type Ada du modèle de données
-- le bouton Fermer de la région 2 ne doit pas être activable
2. -- appel de la procédure du noyau fonctionnel.
consulterAuteur (autIdf, listeOuvVal ) ;
3. -- les boutons Rechercher/Annuler de la région 1 ne doivent pas être activables, …
-- préparation de la tree_view pour afficher la liste des ouvrages
creerColonne ("référence" , treeViewOuvrage, false); -- 3 fois pour les 3 colonnes sans titrecreerModele (treeViewOuvrage, modeleOuvrage) ; -- associe le modèle à la vue
-- modeleOuvrage : Gtk_Tree_Store; utilise le module Gtk.Tree_Store
Spécification{ } �{la liste des ouvrages de l’auteur sélectionné est affichée dans la fenêtre}
Réalisation IHM Annie Culet 67
Affichage dans un composant Tree_View
4. -– affichage des valeurs résultat dans la région 2 de la fenêtre dans un composant tree_view
-- la liste chaînée est parcourue avec le module p_list
listeCourante := listeOuvVal;while not estVide (listeCourante) loop
ouvVal := premier (listeCourante);-- ajout d’une ligne dans le modèle
append (modeleOuvrage, rangOuvrage, null_iter); -- rang désigne une ligne-- alimente les 3 colonnes de la ligne avec une valeur de type chaîne
set(modeleOuvrage, rangOuvrage, 0, ouvVal.refOuvrage); --colonne 1set(modeleOuvrage, rangOuvrage, 1, ouvVal.titreOuvrage); -- colonne 2set(modeleOuvrage, rangOuvrage, 2, p_conversionGenre.to_string(ouvVal.genreOuvrage));listeCourante := suite(listeCourante);
end loop;
-- une fois le modèle alimenté, la vue s’affiche automatiquement
-- rangOuvrage : Gtk_Tree_Iter := Null_Iter; utilise le module Gtk.Tree_Model;
-- le module p_conversionGenre instancie le module générique p_enum du module p_conversion pour le type tgenreOuvrage;
Réalisation IHM Annie Culet 68
Sélection dans un composant Tree_View
-- des valeurs sont affichées dans une ou plusieurs colonnes d’une Tree_View
-- récupération de la ligne sélectionnée (rang)
Get_Selected (Get_Selection (treeviewOuvrage), Gtk_Tree_Model(modeleOuvrage), rangOuvrage);
-- récupération de la valeur de la colonne 1 dans la ligne (une chaîne)
-- et conversion au type Ada
to_ada_type (Get_String (modeleOuvrage, rangOuvrage, 0), ouvVal.refOuvrage);
-- utlise le module Gtk.Tree_Selection
… on veut récupérer la référence de l’ouvrage dans la ligne sélectionnée de la tree_view précédente.
La réalisation Annie Culet 69
Test de logiciel
Pourquoi ?
Réutilisation dans Ariane 5 d’un composant logiciel d’Ariane 4.
... une seule petite variable : l'accélération horizontale.
l'accélération maximum d'Ariane 4 était d'environ 64
l'accélération maximum d'Ariane 5 était d'environ 300
la variable codée sur 8 bits a connu un dépassement de capacité …
KOUROU, Guyane 4 Juin 1996
37 secondes après le décollage
la fusée Ariane 5 explose en
plein ciel à 4000 m d'altitude.
La réalisation Annie Culet 70
Définition du test� Ce que n’est pas le test :
un moyen de s’assurer qu’un logiciel fonctionne.
� Ce qu’est le test :
� un processus - manuel ou automatique- dont le but est de mettre en évidence les
défauts d’un logiciel.
Défaut,anomalie : manifestation d’une erreur lors de
l’exécution
Erreur : écart entre une valeur (ou condition) observée ou
mesurée et la valeur (ou condition) spécifiée ou th éoriquement
correcte
� une activité destructrice : un bon test est un essai qui trouve des erreurs. � une activité centrale dans le processus de développement
La réalisation Annie Culet 71
Test et qualité du logiciel� Le test, une activité non valorisante ?
� La majorité des logiciels ne sont pas suffisamment testés.
� Le test n’est pas du debugging:
� le test ne doit pas être fait par les développeurs (sauf tests unitaires)
� son objectif n’est pas de localiser les erreurs, ni de les corriger
� L’objectif du test n’est pas de montrer que le logiciel ne comporte plus d’erreurs
� il restera toujours des erreurs !
� le test est un moyen d’atteindre un niveau de qualité du logiciel. Celui-ci dépend du
contexte d ’utilisation du logiciel.
� Le test exhaustif d’un logiciel est impossible.
La réalisation Annie Culet 72
Coût du test
Une grande partie des activités de test est effectuée en fin de développement et plus une faute est détectée tard, plus elle coûte cher… dans un contexte où les testeurs accumulent
tous les retards pris par les phases précédentes.
L’activité de test
composante à part entière dans le cycle de développement d’un logiciel
AnalyseConception40%
Codage20%
TestInstallation
40%
En moyenne, on trouve entre 100 et 150 erreurs pour chaque millier de lignes de code écrites à la main.
La réalisation Annie Culet 73
Niveaux de test et cycle de vie
Codage
Tests unitaires
Tests d’intégration
Tests de validation
Tests de recette
Plans de tests Résultats des tests
Conception détaillée
Conception globale
Spécification
Expression des besoins
La réalisation Annie Culet 74
Niveaux de test� Test unitaire
test d’un composant du logiciel pris isolément (par ex. une procédure, un module, ...)
� Test d’intégration
succession de tests dans laquelle les composants sont progressivement assemblés en
suivant la relation d’utilisation. Vérification des spécifications fonctionnelles et
techniques. Test des interfaces entre composants.
� Test de validation
vérification de la conformité du produit final aux spécifications.
� Test de recette
test dans les locaux de l’acquéreur pour vérifier que les dispositions contractuelles
sont bien vérifiées.
La réalisation Annie Culet 75
Test de non régression
� Problématique : "Je ne comprends pas. Hier ça marchait …" Il est très souvent impossible de répondre.
La non régression du code (une nouvelle version du code est au moins aussi bonne que
la précédente) est la certitude que l’on avance.
� Test de non régression : test effectué suite à une modification afin de montrer que
l’ensemble n’a pas été affecté (ex. par un effet de bord non prévu).
� Comment ? : tester la nouvelle version du composant ou d’un ensemble de
composants avec le jeu de test précédent.
� Quand? : tests unitaires, test d’intégration
La réalisation Annie Culet 76
Tests statiques / dynamiques
� Statiques
inspection : examen du code sans exécution par des personnes n’ayant pas participé
au codage
� méthode efficace et peu coûteuse
� nécessaire mais pas suffisante
� ne permet pas de valider le comportement du logiciel
� risque de provoquer des tensions
� Dynamiques
exécution du programme avec un jeu de test
Ces 2 méthodes sont complémentaires
La réalisation Annie Culet 77
Test dynamique : familles� Test fonctionnel (test de boite noire)
à partir des spécifications (informelles, semi-formelles, formelles). L’implémentation
des composants n’est pas connue (le code n’est pas utilisé pour sélectionner les
données de test).
objectif : mettre en évidence un écart entre le comportement réel et le comportement spécifié d’un programme ou d’un composant.
� Test structurel (test de boite blanche)
à partir de la structure interne du programme.
C’est l’implémentation qui est testée.
objectif : couvrir tous les ‘chemins’ possibles dans le code afin mettre en évidence des erreurs dans l’exécution du programme.
� Test de performance
évaluation des performances d'un système par simulation des conditions réelles d'utilisation
� Test IHM (interface homme-machine)
test du comportement de l’interface (menu, fenêtre, navigation ...)
La réalisation Annie Culet 78
Test dynamique : étapes� Formulation d’une stratégie de test
� niveaux de test à appliquer et leurs objectifs
� méthodes, techniques, outils
� Définition des plans de test
� quels composants, dans quel ordre ?
� comment appliquer la stratégie ? (tests des versions successives, critères d’arrêt des
tests, …)
� Description de fiches de test (valeurs à l’entrée du test, sorties attendues)
� Exécution des fiches de test spécifiées
� Observation des résultats
La réalisation Annie Culet 79
Les difficultés du test� Comment déterminer les jeux de test pertinents ?
� sélection des tests assurant un bon échantillonnage des cas possibles (entrées
valides, invalides, inattendues, aux limites) : couverture du domaine des entrées.
� Comment déterminer les oracles des tests ?
� décider si le test est un échec ou un succès (rôle de l ’oracle)
� comparaison des résultats observés avec ceux attendus
� (les résultats - valeurs, propriétés- ne sont pas forcément facilement accessibles …)
� Comment décider de l’arrêt des tests ?
� impossible de tester un système de façon exhaustive
� nécessité de définir des critères d’arrêt
La réalisation Annie Culet 80
Méthode fonctionnelle (1)
� Tests boite noire : tests conçus à partir de la spécification du programme (la structure
du programme est inconnue) : Pour chaque fonctionnalité requise d’un programme,
sélection d’un jeu de tests
� Objectif : tester tous les comportements du programme afin de détecter les écarts par
rapport au comportement spécifié (fonctions non conformes ou manquantes) . Vérifier les
critères qualité (sécurité, portabilité, …)
Difficulté de mettre en œuvre des méthodes systématiques lorsque les spécifications ne
sont pas écrites dans un langage formel.
La réalisation Annie Culet 81
Méthode fonctionnelle (2)
1. A partir de la spécification, identification des fonctionnalités élémentaires pouvant
être testées séparément.
Exemple : programme réalisant les opérations courantes sur un compte bancaire
Fonctionnalités :
créditer (compte, montant) retourner solde
débiter (compte, montant) retourner solde
consulter (compte) retourner solde
-- solde : solde du compte
-- montant : montant de dépôt ou du retrait
La réalisation Annie Culet 82
2. Pour chaque fonctionnalité, sélectionner un jeu de tests garantissant une bonne
couverture du domaine des entrées :
dans les limites fonctionnelles, hors limites, aux limites.
débiter (compte : COMPTE, montant : réel+ ) retourner solde : réel
contraintes :
solde >= solde-min du compte (découvert maximum autorisé)
montant <= montant-max du compte (retrait maximum autorisé)
conditions préalables : compte ∈∈∈∈ COMPTE ; solde initial >= solde-min
précondition : montant<=montant-max
traitement solde final := solde initial - montant
postcondition : solde final >= solde-min
Méthode fonctionnelle (3)
La réalisation Annie Culet 83
Méthode fonctionnelle (4)Sélection du jeu de test :
Découper le domaine des entrées en classes d’équivalence de façon à ce que chaque
classe corresponde aux valeurs faisant l’objet d’un traitement différent dans la
spécification.
Pour chaque classe : sélectionner un élément
déterminer la valeur des sorties attendues
valeur non valide
valeur non valide
montant
montant-max
x
valeur valide
réel
0
x x
valeur spéciale
x
Domaine du montant du retrait
La réalisation Annie Culet 84
Méthode fonctionnelle (5)Rajouter les valeurs aux bornes des entrées (limites)
x
valeur valide extrême
x
valeur spéciale
montant
montant-max
x x
valeur valide
valeur non valide
réel
0
x
valeur non valide
Solde initial - montant = solde-min
x
opération refusée
solde initial
solde-min
xx
valeur valide
réelxvaleur valide extrême
Domaine du montant du retrait
Domaine du solde
La réalisation Annie Culet 85
Méthode fonctionnelle (6)
Solde init Solde-min montant max-retrait Solde final
500 -300 -200 700 500
500 -300 0 700 500
500 -300 300 700 200
500 -300 700 700 -200
Cas
mont < 0
mont = 0
normal
limite
Résultat attendu
refus
refus
OK
OK
500 -300 900 700 500 non valide refus
-200 -300 400 700 -200
100 -300 400 700 -300
200 -300 400 700 -200
non valide
limite
valide
refus
OK
OK
-300 -300 400 700 -300 non valide refus
La réalisation Annie Culet 86
Méthode fonctionnelle (7)
100
-200
--300300
-200 300 400 900 montant
Solde initial
00
200
Domaine de valeurs valides
500
700700
OK
refus
2 jeux de valeurs pour le même cas
La réalisation Annie Culet 87
Quand écrire les tests unitaires?
� L'une des pratiques les plus en vogue aujourd'hui, l'eXtreme Programming, nous dit de les écrire avant même de commencer à coder.
� Avant d'écrire un composant avec des défauts
� d'abord se demander ce que le composant de programme doit faire et écrire un
programme de test
� puis réaliser le composant et donc constater que celui-ci a des défauts parce que le test unitaire ne passe pas. Mais ….
� Avantages
� réfléchir aux pré et post conditions
� préciser les règles de validité de paramètres
� éviter d’écrire du code inutile
La réalisation Annie Culet 88
AUnit� Aunit : Framework* de tests unitaires et de non régression
� ensemble de modules Ada de la famille xUnit (Junit, Cunit, ...)
� facilite la création et l’exécution de tests pour des applications Ada
� beaucoup plus qu’une alternative aux "println" !
� Principe :
� Tests avec entrées normales, limites, hors limites et comparaison avec résultats
attendus
*Framework : ossature générale d’une application offrant une partie commune de
traitements et que chaque utilisateur personnalise pour son cas particulier.
La réalisation Annie Culet 89
Aunit : la non régression� Cas de test (Test_Case)
module contenant un ensemble de procédures de test
+ Set_Up : procédure exécutée avant l’appel d ’une procédure de test
+ Tear_Down : procédure exécutée après l’exécution d’une procédure de test
� Suite de tests (Test_Suite)
empilement de Test_Case
� Lancement des tests (Test_Run)
lancement d’une suite de tests avec édition d’un rapport de test
Test_Case
Test_Suite
Proc. de test
Proc. de test
Proc. de test
T_C T_C
P
PP
P
P
P
P
Set_Up Tear_Down S_U T_D S_U T_D
La réalisation Annie Culet 90
Test_Case (1)with AUnit.Test_Cases.Registration; use AUnit.Test_C ases.Registration;
with AUnit.Assertions; use AUnit.Assertions;
-- modèle pour un module cas de test
package body p_ XXXX is
-- préparation : exécutée avant chaque procédure de te st
procedure Set_Up (T : in out Test_Case) isbegin-- effectuer toutes les initialisations nécessaires
null;
end Set_Up ;
-- exécutée après chaque procédure de test procedure Tear_Down (T : in out Test_Case) isbegin-- remettre au propre l’environnement pour la procédur e de test suivante
null;
end Tear_Down;
La réalisation Annie Culet 91
Test_Case (2)-- procédure de test. Autant de procédures que néces saires
procedure Test 1 (R : in out AUnit.Test_Cases.Test_Case'Class) isbegin-- le corps de la procédure de test
null;-- teste les résultats attendus-- plusieurs assertions et actions sont possibles
Assert (boolean_Expression, "indication de l’erreur");
end Test 1;
Les assertions :
Aunit.Assertions.Assert(boolean_Expression, descrip tion);
description : chaîne éditée dans le rapport lorsque l’expression booléenne est fausse
Exemple : dans une procédure de test de l’opération addition
x:= 12; y := 14;assert (x + y = 26, "l’addition est incorrecte");
La réalisation Annie Culet 92
Test_Case (3)
-- Enregistrement des procédures de test
procedure Register_Tests (T : in out Test_Case) is
begin-- à répéter pour chaque procédure de test
Register_Routine (T, Test 1'Access ,"nommage de la procédure de test");
end Register_Tests;
-- Identification du cas de test
function Name (T : Test_Case) return String_Access is
beginreturn new String' ("nom du cas de test");
end Name;
end p_ XXXX;
La réalisation Annie Culet 93
Suite de testswith AUnit.Test_Suites; use AUnit.Test_Suites;
-- Liste des cas de test à empiler
with p_ XXXX;
with p_ YYYY;
function Une_Test_Suite return Access_Test_Suite is
Result : Access_Test_Suite := new Test_Suite;
begin
-- ajout des cas de test
Add_Test (Result, new p_ XXXX.Test_Case);
Add_Test (Result, new p_ YYYY.Test_Case);
return Result;
end Une_Test_Suite ;
La réalisation Annie Culet 94
Lancement d’une suite de tests
with AUnit.Test_Runner;
-- Suite de tests
with Une_Test_Suite;
procedure Test_Run is
procedure Run is new AUnit.Test_Runner ( Une_Test_Suite );
begin
Run (Timed => False);
end Test_Run ;
Note : Il est possible d ’empiler plusieurs suites de tests.
Cette possibilité n ’est pas illustrée dans ce cours.
La réalisation Annie Culet 95
ExempleOpération de débit sur un compte bancaire-1
Les contraintes : si ces conditions ne sont pas satisfaites, le compte n’est pas débité.
le découvert maximum autorisé sur le compte est de 300 €
le retrait maximum autorisé est de 700 €.
subtype tcompteNumero is string (1 .. 10) ;subtype tsolde is float ;subtype tretrait is float range 0.0 .. float’last ;
type tcompte is recordnumero : tcompteNumero ;solde : tsolde ;
end ;procedure debiter(compte:in out tcompte; montant:in tretrait);
-- condition préalable à l’opération : compte.solde >= -300-- précondition de l’opération : montant <= 700-- postcondition de l’opération : compte.solde >= -300
ExRetraitImpossible:exception ; -- levée dans tous les cas où le retrait est refusé ; l e solde reste identique.
procedure initialiserCompte (compte:in out tcompte; m ontant:in tsolde);
-- initialise le solde du compte avec montant (>=-30 0)
La réalisation Annie Culet 96
ExempleOpération de débit sur un compte bancaire-2
package body p_TestDebitSoldeInitConstant is
leCompte : tcompte;
procedure Set_Up (T : in out Test_Case) isbegin
initialiserCompte (leCompte, 500);end Set_Up;
procedure Tear_Down (Test : in out Test_Case) isbegin
null;end Tear_Down;
function Name (T : Test_Case) return String_Access isbegin
return new String'(" Test de l’opération de débit en faisant varier le retrait ");
end Name;
La réalisation Annie Culet 97
ExempleOpération de débit sur un compte bancaire-3
-- cas normal. Retrait possibleprocedure test _debitOk1 (T : in out AUnit.Test_Cases.Test_Case'Class) is
begindebiter (leCompte, 300) ;assert (leCompte.solde=200, “débit non correctement effectué”) ;
exception when ExRetraitImpossible => assert (false, “le retrait devrait être effectué”) ;
end test_ debitOk1;
-- cas où le montant du retrait dépasse le maximum a utoriséprocédure test_ debitRefus1 (t : in out Aunit.Test_Cases.Test_Case’Class) is
begindebiter (leCompte, 900) ;assert (false, “montant retrait trop important non t raité”) ;
exception when ExRetraitImpossible => assert (leCompte.solde=500 ,“le solde devrait rester identique”);
end test_ debitRefus1 ;
La réalisation Annie Culet 98
-- cas où le montant du retrait est égal au maximum autorisé
procédure test_ debitLimite1 (t : in out Aunit.Test_Cases.Test_Case’Class) is
begindebiter (leCompte, 700) ;assert (leCompte.solde = -200, “débit non correctemen t effectué”) ;
exception when ExRetraitImpossible => assert (false, “le retrait devrait être effectué”) ;
end test_ debitLimite1 ;
procedure Register_Tests (T : in out Test_Case) is
beginRegister_Routine(T,Test_ DebitOk1 'Access, " Test retrait cas normal ");Register_Routine(T,Test_ DebitRefus1 'Access, " Test retrait trop important ");Register_Routine(T,Test_ DebitLimite1 'Access," Test retrait limite autorisée ");
end Register_Tests;
end p_TestDebitSoldeInitConstant ;
ExempleOpération de débit sur un compte bancaire-4
La réalisation Annie Culet 99
package body p_TestDebitRetraitConstant is
leCompte : tcompte; retrait : tretrait ;
procedure Set_Up (T : in out Test_Case) isbegin
retrait := 400;end Set_Up;
procedure Tear_Down (Test : in out Test_Case) isbegin
null;end Tear_Down;
function Name (T : Test_Case) return String_Access isbegin
return new String'(" Test de l’opération de débit en faisant varier le solde initial ");
end Name;
ExempleOpération de débit sur un compte bancaire-5
La réalisation Annie Culet 100
-- cas où le montant du découvert autorisé est dépas sé
procédure test_ debitRefus2 (t : in out Aunit.Test_Cases.Test_Case’Class) is
begininitialiserCompte (leCompte, -200);debiter (leCompte, retrait) ;assert (false, “ cas découvert non autorisé non trait é ”) ;
exception when ExRetraitImpossible => assert (leCompte.solde = -2 00, “ le solde devrait rester identique ”) ;
end test_ debitRefus2 ;
-- cas où le montant du découvert autorisé est attei nt
procédure test_ debitLimite2 (t : in out Aunit.Test_Cases.Test_Case’Class) is
begininitialiserCompte (leCompte, 100);debiter (leCompte, retrait) ;assert (leCompte.solde = -300, “débit non correctemen t effectué”) ;
exception when ExRetraitImpossible => assert (false, “le retrait devrait être effectué”) ;
end test_ debitLimite2;
ExempleOpération de débit sur un compte bancaire-6
La réalisation Annie Culet 101
ExempleOpération de débit sur un compte bancaire-7
-- cas normal : le montant du découvert n’est pas dé passé
procédure test_ debitOk2 (t : in out Aunit.Test_Cases.Test_Case’Class) is
begininitialiserCompte (leCompte, 200);debiter (leCompte, retrait) ;assert (leCompte.solde = -200, “débit non correctemen t effectué”) ;
exception when ExRetraitImpossible => assert (false, “le retrait devrait être effectué”) ;
end test_ debitOk2 ;
procedure Register_Tests (T : in out Test_Case) is
beginRegister_Routine(T,Test_ DebitOk2 'Access, " Test cas normal ");Register_Routine(T,Test_ DebitRefus2 'Access, " Test découvert non autorisé ");Register_Routine(T,Test_ DebitLimite2 'Access," Test découvert limite ");
end Register_Tests;
end p_TestDebitRetraitConstant ;
La réalisation Annie Culet 102
Méthodes structurelles (1)
� tests boite blanche : tests conçus à partir de la structure du code (flot de contrôle, flot
de données):
Objectif : détecter les fautes d’implémentation
� Couverture du flot de contrôle
toutes les instructions, conditions, chemins exécutables, …
� Couverture du flot de données
toutes les définitions de variables, leurs utilisations, …
La réalisation Annie Culet 103
Méthodes structurelles (2)� But : produire des données de test qui exécuteront un ensemble de comportement du
programmeUn programme => un graphe de contrôle
beginif (x<=0) then x:= -xelse x:= 1-x;if (x=-1) then x:=1else x:=x+1;end
a
b c
d
f e
g
x<=0 x>0
x:= -x x:= 1-x
x=-1 x!=-1
x:=1 x:=x+1
Graphe orienté :un noeud : un bloc d’instructionsun arc : la possibilité de transfert de l’exécution d’un noeud à un autre
Une exécution possible = un chemin dans le graphe de contrôleacdeg est un chemin de contrôlebdfg n’est pas un chemin de contrôle
La réalisation Annie Culet 104
Méthodes structurelles (3)L’ensemble des chemins de contrôle du graphe =
abdfg + abdeg + acdfg + acdeg
a
b c
d
f e
g
x<=0 x>0
x:= -x x:= 1-x
x=-1 x!=-1
x:=1 x:=x+1Construction de l’expression des chemins :
séquentielle alternative itérative
a
b
a
b c
d
a
b
c
ab abd + acd a(ba)*c
La réalisation Annie Culet 105
Méthodes structurelles (4)Chemin exécutable – non exécutable
Donnée de test (DT1) : { x=2 }
� DT1 sensibilise le chemin acdfg : acdfg est un chemin exécutable
� abdfg est un chemin non exécutable : aucune DT capable de sensibiliser ce chemin
� sensibiliser un chemin : problème difficileintérêt des outils automatiques (mais attention problème de trouver des DT qui sensibilisent un chemin est non décidable)
Existence de chemins non exécutables :souvent signe de mauvais codage
a
b c
d
f e
g
x<=0 x>0
x:= -x x:= 1-x
x=-1 x!=-1
x:=1 x:=x+1
La réalisation Annie Culet 106
Méthodes structurelles (5)Hiérarchie des techniques de tests structurels
� Couverture de tous les noeuds
sensibiliser tous les chemins de contrôle qui nous permettent de visiter tous les nœuds du graphe de contrôle.
� Couverture de tous les arcs
sensibiliser tous les chemins de contrôle qui nous permettent de visiter tous les arcs du graphe de contrôle.
« tous les arcs» est plus fiable (plus fort) que «tous les nœuds»
� Couverture de tous les chemins
critère le plus fort : impossible à appliquer dès que le code contient un boucle (+ problème des chemins non exécutables)
La réalisation Annie Culet 107
Méthodes structurelles (6)
Le critère de sélection du jeu de tests repose sur le code du programme
� Limitations :
un jeu de tests de taille raisonnable couvrant tous les chemins exécutables est
utopique
les oublis par rapport à la spécification ne sont pas détectés
lors d’une modification du programme, difficile de réutiliser les tests précédents
(problème de non-régression)
� Intérêt :
quantifiable, automatisable
nombreux outils disponibles (Logiscope Verilog, Attol, ...)