À l’initiateur de ce cours, Jean-Michel Inglebert…
1. Gestion de projets
1.1. C’est quoi 1 projet ?
On appelle projet un ensemble finalisé d’activités et d’actions entreprises dans le but de répondre à un besoin défini dans des délais fixés et dans la limite d’une enveloppe budgétaire allouée.
Wikipedia
- Répondre au client
(qui paye)
- dans la vraie vie
(les ressources sont limitées ET les obstacles matériels et humains sont sans limite)
JMI
1.2. OK, allons-y …
-
DEBUT : la demande est exprimée/fournie comment ?
-
cahier des charges,
-
liste de fonctionnalités attendues
-
…
-
-
FIN : le résultat sera livré quand ?
Eviter de répondre : hier (Sinon, c’est un processus de sauvetage et plus 1 projet)
1.3. … entre DEBUT et FIN, comment FAIRE ?
- Option 1
-
se débrouiller (comme dans 'FAIRE les soldes')
- Option 2
-
dérouler un process (comme dans '(RE)FAIRE son passeport')
- Option 3
-
réaliser un projet
1.4. Depuis le temps que la notion de projet existe …
7 projets sur 10 arrivent en retard ou jamais !
Revue Programmez!
Les causes :
-
la demande client est floue
-
surestimation des compétences disponibles
-
dérive au démarrage
-
les demandes de modification se sont perdues
-
les équipes sont surchargées
-
perte de compétences au cours du projet
-
les risques sont mal évalués
-
les réunions sont improductives
Dans tous les cas, c’est l’équipe de développement qui est fautive.
1.5. Pourquoi est-ce si difficile ?
Pourtant :
-
L’objectif est connu (fourni par le client)
-
L’atteinte de l’objectif est mesurable (évaluable)
Le problème
On n’a pas de méthode infaillible qui garantisse qu’une équipe projet atteindra les objectifs. |
Pourquoi ?
Il s’agit de gérer une complexité métier, technique et humaine avec des ressources limitées (temps, moyens …) |
Seule certitude prévisible : il faudra s’adapter aux imprévus.
1.6. Le découpage en phases d’un projet logiciel
Ça n’empêche pas de définir de grandes étapes génériques que le projet devra franchir : ses PHASES
Un projet de développement logiciel se terminera de 'manière satisfaisante' si l’équipe fournit une application fiable, robuste et maintenable qui répond aux exigences du client. |
- fiable
-
fait ce qu’on attend d’elle dans les conditions fixées
- robuste
-
supporte la charge, les erreurs des utilisateurs, les pannes etc
- maintenable
-
ne demande pas d’être réécrite pour la moindre évolution
2. Produire des applications
3. Méthodes
3.1. R.A.C.H.E
La méthode préférée des étudiants La RACHE!
Voir l’excellent site parodique http://www.la-rache.com/. |
3.2. Méthodes en Cascade ou Cycle en V
Le cycle en V adopte :
-
une approche descendante pour l’analyse et la conception
-
suivie d’une phase montante pour le codage, les tests et l’intégration.
Un projet de développement de type cycle en V se terminera de manière satisfaisante si l’équipe arrive à remonter le 'V' dans de 'bonnes conditions'. |
Arrêt du projet
|
Dérive temporelle
|
En résumé :
-
illustre une approche cartésienne de réduction de la complexité
-
produit les plans de tests dans les phases descendantes
-
vérifie les plans de tests dans les phases ascendantes
Limites connues
|
3.3. Du cycle en V aux cycles en W
3.3.1. Partons d’un exemple trivial
Développer une application web capable de collecter et d’afficher les urls collectées.
Cycle 0
Mise en place d’une infrastructure d’intégration
Solution retenue :
-
Langage de développement :
PHP
-
Infrastructure d’intégration : serveur
LAMP
Cycle 1
Afficher les urls collectées
Solution retenue :
-
le serveur maintient un tableau d’urls
-
la page retournée en mode GET affiche les urls sous forme de liste
Cycle 2
Saisie et collecte des urls
Solution retenue :
-
la page propose un formulaire de saisie de nouvelles urls
3.3.2. Bilan de l’exemple trivial
3.3.3. Les bénéfices reconnus
|
Rappel :
Un projet de développement logiciel se terminera de 'manière satisfaisante' si l’équipe fournit une application fiable, robuste et maintenable qui répond aux exigences du client.
Les cas où ça ne marche pas
|
Les problèmes nouveaux
|
3.4. V versus W
V
|
W
|
3.5. Ce qui est toujours fait
-
Planifier (tout ou partie) des prochaines actions
-
Réaliser les actions planifiées
-
Evaluer la distance au but
3.6. Evaluer l’atteinte des objectifs
L’atteinte de tous les objectifs (et sous-objectifs) d’un projet doit être évaluée.
Quelle que soit la 'méthode de développement' employée, tout développement logiciel doit réaliser des 'tests unitaires' et des 'tests d’intégration'. |
Aujourd’hui, ON DOIT
|
4. La méthode Agile SCRUM
-
Scrum est un processus agile qui permet de produire la plus grande valeur métier dans la durée la plus courte.
-
Du logiciel qui fonctionne est produit à chaque sprint (toutes les 2 à 4 semaines).
-
Le métier définit les priorités.
-
L’équipe s’organise elle-même pour déterminer la meilleure façon de produire les exigences les plus prioritaires.
-
À chaque fin de sprint, tout le monde peut voir fonctionner le produit courant et décider soit de le livrer dans l’état, soit de continuer à l’améliorer pendant un sprint supplémentaire.
4.1. Les Acteurs SCRUM
Les acteurs dans Scrum sont :
- le Product Owner
-
le client ou son représentant qui fait partie de l’équipe
- le SCRUM Master
-
un animateur, facilitateur plutôt qu’un chef (de projet)
- l'équipe
-
capable de mettre en oeuvre toutes les compétences (architecture, conception, développement, IHM, tests, documentation, etc.)
4.1.1. Rôles du Product Owner
-
Définit les fonctionnalités du produit
-
Choisit la date et le contenu de la release (version livrée aux utilisateurs réalisée en plusieurs sprints)
-
Est responsable du retour sur investissement (de la valeur métier)
-
Définit les priorités dans le backlog en fonction de la valeur « métier »
-
Ajuste les fonctionnalités et les priorités à chaque sprint si nécessaire
-
Accepte ou rejette les résultats
4.1.2. Rôles du SCRUM Master
-
Assure le management du projet
-
Fait appliquer par l’équipe les valeurs et les pratiques de Scrum
-
Résout des problèmes
-
S’assure que l’équipe est complètement fonctionnelle et productive
-
Facilite une coopération poussée entre tous les rôles et fonctions
-
Protège l’équipe des interférences extérieures
4.1.3. L’équipe SCRUM
-
Comprend de 5 à 10 personnes, de préférence à plein temps sur le projet
-
Regroupe tous les rôles Architecte, concepteur, développeur, spécialiste IHM, testeur, etc.
-
S’organise par elle-même
-
La composition de l’équipe ne doit pas changer pendant un Sprint
4.2. Déroulement d’un projet SCRUM
-
Planification du sprint suivant
-
Choix des items du backlog de produit qui seront réalisés durant le sprint
-
Identification des tâches et définition du backlog de sprint estimé en heures
-
-
SCRUM quotidien (15mn)
-
qu’ai-je fait hier ?
-
que vais-je faire aujourd’hui ?
-
y-a-t-il des problèmes particuliers ?
-
-
Revue de SPRINT (15-30mn)
-
Présentation de l’application à l’équipe et à toute personne invitée
-
Calcul de la vélocité de l’équipe
-
-
Rétrospective de SPRINT (15mn)
-
L’équipe (client compris) revient sur ses modes de fonctionnement
-
Qu’est-ce qui marche : on continue à l’identique
-
Qu’est-ce qui ne marche pas : comment fait-on désormais ?
-
Y-a-t-il des choses à ne plus faire ?
-
-
Planification de Release
-
Définit les exigences du backlog de produit qui constitueront la release
-
Définit le nombre de sprints et la date de livraison de la release
-
Effectuée avant le premier sprint si la vélocité de l’équipe est connue
-
Actualisée à chaque fin de sprint
-
4.3. Les artefacts SCRUM
- Le backlog de produit
-
la liste des exigences métier ordonnée par la valeur métier (l’importance métier pour le Product Owner)
- Les backlogs de sprint
-
les listes des tâches à réaliser pour répondre aux exigences métier choisies pour le sprint
- Les burndowns du projet
-
les graphiques montrant l'avancement du projet
4.3.1. Le backlog de produit
-
Il est fourni par le Product Owner au démarrage du projet
-
Il est une liste ordonnée d’exigences (stories) classées selon leur importance métier
-
Le classement de chaque exigence pourra être révisé à chaque revue de sprint par le Product Owner
-
Des exigences pourront être ajoutées/retirées à chaque revue de sprint par le Product Owner
4.3.2. Les backlogs de sprint
-
Définis lors de la planification des sprints
-
Ensemble des tâches à effectuer pour réaliser les exigences choisies pour le sprint dans le backlog de produit
-
Les tâches ont estimées en heures
-
L’estimation des tâches sont réestimées à chaque SCRUM (mélée quotidienne)
-
La définition des tâches conduit souvent à réaliser collectivement une conception de haut niveau
4.3.3. Les burndowns du projet
-
Les valeurs du backlog de produit et les estimations du backlog de sprint permettent de produire les courbes d’avancement du projet
Burndown de sprint
-
Actualisé chaque jour
-
Indique le nombre d’heures restantes pour toutes les tâches du sprint
le burndown de sprint n’est pas nécessairement décroissant si des tâches sont ajoutées ou bien réévaluées à la hausse pour leur durée. |
Burndown de produit (ou de release)
-
Évalue à chaque fin de sprint la quantité du backlog de produit qui reste à faire
le backlog de release pouvant évoluer, la courbe n’est pas nécessairement décroissante. |
permet d’en déduire une estimation de la date de fin de release ou une estimation de la quantité du backlog de produit qui ne sera pas réalisée à une date donnée. |
4.3.4. Indicateurs
- Vélocité
-
quantité du backlog de produit réalisée par l’équipe pendant un sprint (calculée en points)
- Capacité
-
quantité du backlog de produit qui sera réalisée lors du prochain sprint
5. Les Tests
Quelle que soit la méthode de développement choisie, les Tests sont le seul moyen de garantir que le produit livré est conforme aux exigences du client.
5.1. Tests unitaires
Ce sont les plus simples. Et pourtant il s’agit …
-
d’oublier les approches 'manuelles'
-
d’expliciter les 'limites'
-
de traiter le 'qualitatif' et le 'quantitatif'
Spécifications
Opérations
Préconditions
|
import junit.textui.TestRunner;
import junit.framework.TestSuite;
import junit.framework.TestCase;
public class MatriceEntierOperationsTest extends TestCase {
static int totalAssertions = 0;
static int bilanAssertions = 0;
/*
Types des opérations du type MatriceEntier
*/
public void test_type_new_MatriceEntier() throws Exception {
MatriceEntier m = new MatriceEntier(3,3) ;
totalAssertions++ ;
assertEquals("new MatriceEntier(3,3) retourne une MatriceEntier", "MatriceEntier", m.getClass().getName());
bilanAssertions++ ;
}
public void test_type_get() throws Exception {
MatriceEntier m = new MatriceEntier(3,4) ;
totalAssertions++ ;
assertTrue("getNbLignes() > 0", m.getNbLignes() > 0);
bilanAssertions++ ;
totalAssertions++ ;
assertTrue("getNbColonnes() > 0", m.getNbColonnes() > 0);
bilanAssertions++ ;
for (int i=0; i<m.getNbLignes(); i++) {
for (int j=0; j<m.getNbColonnes(); j++) {
totalAssertions++ ;
assertTrue("getElement() retourne un entier", (m.getElement(i,j) >= 0) || (m.getElement(i,j) < 0));
bilanAssertions++ ;
}
}
}
public void test_type_som() throws Exception {
MatriceEntier m = new MatriceEntier(3,4) ;
for (int i=0; i<m.getNbLignes(); i++) {
totalAssertions++ ;
assertTrue("somLigne("+i+") >= 0", m.somLigne(i) >= 0);
bilanAssertions++ ;
}
for (int j=0; j<m.getNbColonnes(); j++) {
totalAssertions++ ;
assertTrue("somColonne("+j+") >= 0", m.somColonne(j) >= 0);
bilanAssertions++ ;
}
}
public void test_type_est() throws Exception {
MatriceEntier m = new MatriceEntier(3,3) ;
totalAssertions++ ;
assertTrue("estCarree() retourne un booleen", (m.estCarree() == true) || (m.estCarree() == false));
bilanAssertions++ ;
totalAssertions++ ;
assertTrue("estDiagonale() retourne un booleen", (m.estDiagonale() == true) || (m.estDiagonale() == false));
bilanAssertions++ ;
m.setElement(0,0,1) ;
totalAssertions++ ;
assertTrue("estDiagonale() retourne un booleen", (m.estDiagonale() == true) || (m.estDiagonale() == false));
bilanAssertions++ ;
m = new MatriceEntier(3,4) ;
totalAssertions++ ;
assertTrue("estCarree() retourne un booleen", (m.estCarree() == true) || (m.estCarree() == false));
bilanAssertions++ ;
}
public void test_type_set_mul() throws Exception {
MatriceEntier m = new MatriceEntier(3,3) ;
for (int i=0; i<m.getNbLignes(); i++) {
for (int j=0; j<m.getNbColonnes(); j++) {
totalAssertions++ ;
assertEquals("setElement() retourne une MatriceEntier", "MatriceEntier", m.setElement(i,j,i+j).getClass().getName());
bilanAssertions++ ;
}
}
totalAssertions++ ;
assertEquals("setPremiereDiagonale(99) retourne une MatriceEntier", "MatriceEntier", m.setPremiereDiagonale(99).getClass().getName());
bilanAssertions++ ;
totalAssertions++ ;
assertEquals("setSecondeDiagonale(99) retourne une MatriceEntier", "MatriceEntier", m.setSecondeDiagonale(99).getClass().getName());
bilanAssertions++ ;
totalAssertions++ ;
assertEquals("mulMatNombre() retourne une MatriceEntier", "MatriceEntier", m.mulMatNombre(33).getClass().getName());
bilanAssertions++ ;
}
/*
main() de la classe de Test
*/
public static void main(String[] args) {
junit.textui.TestRunner.run(new TestSuite(MatriceEntierOperationsTest.class));
if (bilanAssertions == totalAssertions) { System.out.print("Bravo !"); }
else { System.out.print("OUPS !"); }
System.out.println(" "+bilanAssertions+"/"+totalAssertions+" assertions verifiees");
} // fin main
} // fin MatriceEntierOperationsTest
import junit.textui.TestRunner;
import junit.framework.TestSuite;
import junit.framework.TestCase;
public class MatriceEntierPreconditionsTest extends TestCase {
static int totalAssertions = 0;
static int bilanAssertions = 0;
/*
Préconditions du type Pile
*/
public void test_precondition1() {
MatriceEntier m ;
boolean exception = false ;
try { m = new MatriceEntier(0,1) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("new MatriceEntier(0,1) leve une exception", exception);
bilanAssertions++ ;
exception = false ;
try { m = new MatriceEntier(1,0) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("new MatriceEntier(1,0) leve une exception", exception);
bilanAssertions++ ;
exception = false ;
try { m = new MatriceEntier(0,0) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("new MatriceEntier(0,0) leve une exception", exception);
bilanAssertions++ ;
}
public void test_precondition2() throws Exception {
MatriceEntier m = new MatriceEntier(2,3);
boolean exception = false ;
try { m.getElement(-1,1) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("getElement(-1,1) leve une exception", exception);
bilanAssertions++ ;
exception = false ;
try { m.getElement(2,2) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("getElement(2,2) leve une exception", exception);
bilanAssertions++ ;
exception = false ;
try { m.getElement(1,-1) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("getElement(1,-1) leve une exception", exception);
bilanAssertions++ ;
exception = false ;
try { m.getElement(1,3) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("getElement(1,3) leve une exception", exception);
bilanAssertions++ ;
}
public void test_precondition3() throws Exception {
MatriceEntier m = new MatriceEntier(2,3);
boolean exception = false ;
try { m.setElement(-1,1,99) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("setElement(-1,1,99) leve une exception", exception);
bilanAssertions++ ;
exception = false ;
try { m.setElement(2,2,99) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("setElement(2,2,99) leve une exception", exception);
bilanAssertions++ ;
exception = false ;
try { m.setElement(1,-1,99) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("setElement(1,-1,99) leve une exception", exception);
bilanAssertions++ ;
exception = false ;
try { m.setElement(1,3,99) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("setElement(1,3,99) leve une exception", exception);
bilanAssertions++ ;
}
public void test_precondition4() throws Exception {
MatriceEntier m = new MatriceEntier(2,3);
boolean exception = false ;
try { m.somLigne(-1) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("somLigne(-1) leve une exception", exception);
bilanAssertions++ ;
exception = false ;
try { m.somLigne(2) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("somLigne(2) leve une exception", exception);
bilanAssertions++ ;
}
public void test_precondition5() throws Exception {
MatriceEntier m = new MatriceEntier(2,3);
boolean exception = false ;
try { m.somColonne(-1) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("somColonne(-1) leve une exception", exception);
bilanAssertions++ ;
exception = false ;
try { m.somColonne(3) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("somColonne(3) leve une exception", exception);
bilanAssertions++ ;
}
public void test_precondition6() throws Exception {
MatriceEntier m = new MatriceEntier(2,3);
boolean exception = false ;
try { m.setPremiereDiagonale(99) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("setPremiereDiagonale(99) leve une exception", exception);
bilanAssertions++ ;
}
public void test_precondition7() throws Exception {
MatriceEntier m = new MatriceEntier(2,3);
boolean exception = false ;
try { m.setSecondeDiagonale(99) ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("setSecondeDiagonale(99) leve une exception", exception);
bilanAssertions++ ;
}
public void test_precondition8() throws Exception {
MatriceEntier m = new MatriceEntier(2,3);
boolean exception = false ;
try { m.estDiagonale() ; }
catch (Exception e) { exception = true ; };
totalAssertions++ ;
assertTrue("estDiagonale() leve une exception", exception);
bilanAssertions++ ;
}
/*
main() de la classe de Test
*/
public static void main(String[] args) {
junit.textui.TestRunner.run(new TestSuite(MatriceEntierPreconditionsTest.class));
if (bilanAssertions == totalAssertions) { System.out.print("Bravo !"); }
else { System.out.print("OUPS !"); }
System.out.println(" "+bilanAssertions+"/"+totalAssertions+" assertions verifiees");
} // fin main
} // fin MatriceEntierPreconditionsTest
import junit.textui.TestRunner;
import junit.framework.TestSuite;
import junit.framework.TestCase;
public class MatriceEntierAxiomesTest extends TestCase {
static int totalAssertions = 0;
static int bilanAssertions = 0;
/*
Axiomes du type MatriceEntier
*/
public void test_get() throws Exception {
MatriceEntier m = new MatriceEntier(3,4) ;
totalAssertions++ ;
assertEquals("getNbLignes() == 3", 3, m.getNbLignes());
bilanAssertions++ ;
totalAssertions++ ;
assertEquals("getNbColonnes() == 4", 4, m.getNbColonnes());
bilanAssertions++ ;
for (int i=0; i<m.getNbLignes(); i++) {
for (int j=0; j<m.getNbColonnes(); j++) {
totalAssertions++ ;
assertEquals("getElement("+i+","+j+") == 0", 0, m.getElement(i,j));
bilanAssertions++ ;
}
}
m = new MatriceEntier(3,3) ;
totalAssertions++ ;
assertEquals("setPremiereDiagonale(99).getNbLignes() == getNbLignes()", m.setPremiereDiagonale(99).getNbLignes(), m.getNbLignes());
bilanAssertions++ ;
totalAssertions++ ;
assertEquals("setSecondeDiagonale(99).getNbLignes() == getNbLignes()", m.setSecondeDiagonale(99).getNbLignes(), m.getNbLignes());
bilanAssertions++ ;
m = new MatriceEntier(3,3) ;
m.setPremiereDiagonale(99) ;
for (int i=0; i<m.getNbLignes(); i++) {
for (int j=0; j<m.getNbColonnes(); j++) {
totalAssertions++ ;
if ( i == j ) {
assertEquals("getElement("+i+","+i+") == 99", 99, m.getElement(i,i));
} else {
assertEquals("getElement("+i+","+j+") == 0", 0, m.getElement(i,j));
}
bilanAssertions++ ;
}
}
} // fin test_get
public void test_som() throws Exception {
MatriceEntier m = new MatriceEntier(3,4) ;
for (int i=0; i<m.getNbLignes(); i++) {
totalAssertions++ ;
assertEquals("somLigne("+i+") == 0", 0, m.somLigne(i));
bilanAssertions++ ;
}
for (int j=0; j<m.getNbColonnes(); j++) {
totalAssertions++ ;
assertEquals("somColonne("+j+") == 0", 0, m.somColonne(j));
bilanAssertions++ ;
}
m = new MatriceEntier(3,3) ;
m.setPremiereDiagonale(99) ;
for (int ij=0; ij<m.getNbLignes(); ij++) {
totalAssertions = totalAssertions + 2 ; ;
assertEquals("setPremiereDiagonale(99).somLigne("+ij+") == 99", 99, m.somLigne(ij));
bilanAssertions++ ;
assertEquals("setPremiereDiagonale(99).somColonne("+ij+") == 99", 99, m.somColonne(ij));
bilanAssertions++ ;
}
m = new MatriceEntier(3,3) ;
m.setSecondeDiagonale(99) ;
for (int ij=0; ij<m.getNbLignes(); ij++) {
totalAssertions = totalAssertions + 2 ; ;
assertEquals("setSecondeDiagonale(99).somLigne("+ij+") == 99", 99, m.somLigne(ij));
bilanAssertions++ ;
assertEquals("setSecondeDiagonale(99).somColonne("+ij+") == 99", 99, m.somColonne(ij));
bilanAssertions++ ;
}
} // fin test_som
public void test_est() throws Exception {
MatriceEntier m = new MatriceEntier(3,3) ;
totalAssertions++ ;
assertTrue("estCarree() == true", m.estCarree());
bilanAssertions++ ;
m = new MatriceEntier(2,3) ;
totalAssertions++ ;
assertFalse("estCarree() == false", m.estCarree());
bilanAssertions++ ;
m = new MatriceEntier(3,3) ;
totalAssertions++ ;
assertTrue("estDiagonale() == true", m.estDiagonale());
bilanAssertions++ ;
m.setPremiereDiagonale(99);
totalAssertions++ ;
assertTrue("setPremiereDiagonale(99).estDiagonale() == true", m.estDiagonale());
bilanAssertions++ ;
m.setSecondeDiagonale(99);
totalAssertions++ ;
assertFalse("setSecondeDiagonale(99).estDiagonale() == false", m.estDiagonale());
bilanAssertions++ ;
} // fin test_est
public void test_mul() throws Exception {
MatriceEntier m = new MatriceEntier(3,3) ;
m.setPremiereDiagonale(1).mulMatNombre(99) ;
for (int ij=0; ij<m.getNbLignes(); ij++) {
totalAssertions = totalAssertions + 2 ; ;
assertEquals("setPremiereDiagonale(1).mulMatNombre(99).somLigne("+ij+") == 99", 99, m.somLigne(ij));
bilanAssertions++ ;
assertEquals("setPremiereDiagonale(1).mulMatNombre(99).somColonne("+ij+") == 99", 99, m.somColonne(ij));
bilanAssertions++ ;
}
m = new MatriceEntier(3,3) ;
m.setSecondeDiagonale(1).mulMatNombre(99) ;
for (int ij=0; ij<m.getNbLignes(); ij++) {
totalAssertions = totalAssertions + 2 ; ;
assertEquals("setSecondeDiagonale(1).mulMatNombre(99).somLigne("+ij+") == 99", 99, m.somLigne(ij));
bilanAssertions++ ;
assertEquals("setSecondeDiagonale(1).mulMatNombre(99).somColonne("+ij+") == 99", 99, m.somColonne(ij));
bilanAssertions++ ;
}
MatriceEntier m_init = new MatriceEntier(3,3) ;
m = new MatriceEntier(3,3) ;
// Initialise m_init et m à {{0,1,2}{3,4,5}{6,7,8}}
int k = 0 ;
for (int i=0; i<m.getNbLignes(); i++) {
for (int j=0; j<m.getNbColonnes(); j++) {
m_init.setElement(i,j,k) ;
m.setElement(i,j,k) ;
k = k + 1 ;
}
}
m.mulMatNombre(3) ;
for (int i=0; i<m.getNbLignes(); i++) {
for (int j=0; j<m.getNbColonnes(); j++) {
totalAssertions++ ;
assertEquals("m.mulMatNombre(3).getElement("+i+","+j+") == m.getElement("+i+","+j+") * 3", m_init.getElement(i,j) * 3, m.getElement(i,j));
bilanAssertions++ ;
}
}
} // fin test_mul
/*
main() de la classe de Test
*/
public static void main(String[] args) {
junit.textui.TestRunner.run(new TestSuite(MatriceEntierAxiomesTest.class));
if (bilanAssertions == totalAssertions) { System.out.print("Bravo !"); }
else { System.out.print("OUPS !"); }
System.out.println(" "+bilanAssertions+"/"+totalAssertions+" assertions verifiees");
} // fin main
} // fin MatriceEntierAxiomesTest
import junit.textui.TestRunner;
import junit.framework.TestSuite;
import junit.framework.TestCase;
public class MatriceEntierOpSupTest extends TestCase {
static int totalAssertions = 0;
static int bilanAssertions = 0;
/*
Opérations supplémentaires du type MatriceEntier
*/
public void test_toString() throws Exception {
MatriceEntier m = new MatriceEntier(3,3) ;
m.setPremiereDiagonale(1).setSecondeDiagonale(2) ;
String ln = System.getProperty("line.separator") ;
String attendu = "1 0 2 " + ln + "0 2 0 " + ln + "2 0 1 " + ln ;
totalAssertions++ ;
assertEquals("toString() == ", attendu, m.toString());
bilanAssertions++ ;
}
public void test_toHTML() throws Exception {
MatriceEntier m = new MatriceEntier(3,3) ;
m.setPremiereDiagonale(1).setSecondeDiagonale(2) ;
String ln = System.getProperty("line.separator") ;
String attendu = "<table border=\"1\">" + ln ;
attendu += "<tr><td>1</td><td>0</td><td>2</td></tr>" + ln + "<tr><td>0</td><td>2</td><td>0</td></tr>" + ln + "<tr><td>2</td><td>0</td><td>1</td></tr>" + ln ;
attendu += "</table>" + ln ;
totalAssertions++ ;
assertEquals("toHTML() == ", attendu, m.toHTML());
bilanAssertions++ ;
}
/*
main() de la classe de Test
*/
public static void main(String[] args) {
junit.textui.TestRunner.run(new TestSuite(MatriceEntierOpSupTest.class));
if (bilanAssertions == totalAssertions) { System.out.print("Bravo !"); }
else { System.out.print("OUPS !"); }
System.out.println(" "+bilanAssertions+"/"+totalAssertions+" assertions verifiees");
} // fin main
} // fin PileTest
Le programme de test fourni est :
-
un outil de détection des régressions
-
qui pourraient intervenir à la suite d’une modification de la classe
MatriceEntier
-
-
une documentation de spécification
-
précise et concise
-
-
une documentation pour les programmeurs
-
opérationnelle
-
5.2. Tests d’intégration
Plus délicat, il s’agit :
-
de tester les exigences du client
-
de tester les intéractions système
-
de ne pas refaire les tests unitaires
JourSuivantAvecLibDate.class
import junit.textui.TestRunner;
import junit.framework.TestSuite;
import junit.framework.TestCase;
import java.io.*;
public class JourSuivantAvecLibDateTest extends TestCase {
static String programmeATester = "JourSuivantAvecLibDate" ; (1)
Process executionProgrammeATester ; (2)
BufferedReader ecranProgrammeATester ; (3)
BufferedWriter clavierProgrammeATester ; (4)
String finDeLigne = System.getProperty("line.separator") ; (5)
public static void main(String[] args) {
if ( args.length > 0 ) { programmeATester = args[0] ; }
System.out.println("Tests du programme : " + programmeATester);
junit.textui.TestRunner.run(new TestSuite(JourSuivantAvecLibDateTest.class)); (6)
}
protected void setUp() throws IOException { (7)
executionProgrammeATester = Runtime.getRuntime().exec("java -cp . "+programmeATester); (8)
ecranProgrammeATester = new BufferedReader(new InputStreamReader( executionProgrammeATester.getInputStream() )); (9)
clavierProgrammeATester = new BufferedWriter(new OutputStreamWriter( executionProgrammeATester.getOutputStream() )); (10)
}
// Saisies valides
public void test_31_1_2013() throws IOException {
assertEquals("Affiche : 'Saisir une date : jour mois annee ? '","Saisir une date : jour mois annee ? ",ecranProgrammeATester.readLine()); (11)
clavierProgrammeATester.write("31 1 2013"+finDeLigne); (12)
clavierProgrammeATester.flush(); (13)
assertEquals("Affiche : 'Le lendemain du 31/1/2013'","Le lendemain du 31/1/2013",ecranProgrammeATester.readLine());
assertEquals("Affiche : 'sera le 1/2/2013.'","sera le 1/2/2013.",ecranProgrammeATester.readLine()); (14)
}
public void test_28_2_2013() throws IOException {
String messageSaisie = "Saisir une date : jour mois annee ? " ;
String[] ligneJeuDEssai = {"28 2 2013","Le lendemain du 28/2/2013","sera le 1/3/2013."} ;
assertEquals("Affiche : "+messageSaisie,messageSaisie,ecranProgrammeATester.readLine());
clavierProgrammeATester.write(ligneJeuDEssai[0]+finDeLigne); clavierProgrammeATester.flush();
assertEquals("Affiche : "+ligneJeuDEssai[1],ligneJeuDEssai[1],ecranProgrammeATester.readLine());
assertEquals("Affiche : "+ligneJeuDEssai[2],ligneJeuDEssai[2],ecranProgrammeATester.readLine());
}
protected void assertsPourSaisieValide(String messageSaisie,String saisie,String affichage1,String affichage2) throws IOException {
assertEquals("Affiche : "+messageSaisie,messageSaisie,ecranProgrammeATester.readLine());
clavierProgrammeATester.write(saisie+finDeLigne); clavierProgrammeATester.flush();
assertEquals("Affiche : "+affichage1,affichage1,ecranProgrammeATester.readLine());
assertEquals("Affiche : "+affichage2,affichage2,ecranProgrammeATester.readLine());
}
public void test_31_3_2013() throws IOException {
String messageSaisie = "Saisir une date : jour mois annee ? " ;
String[] ligneJeuDEssai = {"31 3 2013","Le lendemain du 31/3/2013","sera le 1/4/2013."} ;
assertsPourSaisieValide(messageSaisie,ligneJeuDEssai[0],ligneJeuDEssai[1],ligneJeuDEssai[2]);
}
// Saisies invalides
protected void assertsPourSaisieInvalide(String messageSaisie,String saisie,String affichage) throws IOException {
assertEquals("Affiche : "+messageSaisie,messageSaisie,ecranProgrammeATester.readLine());
clavierProgrammeATester.write(saisie+finDeLigne); clavierProgrammeATester.flush();
assertEquals("Affiche : "+affichage,affichage,ecranProgrammeATester.readLine());
}
public void test_1_1_1581() throws IOException {
String messageSaisie = "Saisir une date : jour mois annee ? " ;
String[] ligneJeuDEssai = {"1 1 1581","La date du 1/1/1581 n'est pas une date valide."} ;
assertsPourSaisieInvalide(messageSaisie,ligneJeuDEssai[0],ligneJeuDEssai[1]);
}
public void test_32_1_2013() throws IOException {
String messageSaisie = "Saisir une date : jour mois annee ? " ;
String[] ligneJeuDEssai = {"32 1 2013","La date du 32/1/2013 n'est pas une date valide."} ;
assertsPourSaisieInvalide(messageSaisie,ligneJeuDEssai[0],ligneJeuDEssai[1]);
}
} // fin class
1 | nom de l’application (du programme) à tester |
2 | Process (Processus) = programme en cours d’exécution |
3 | lien vers l’écran du programme en cours d’exécution |
4 | lien vers le clavier du programme en cours d’exécution |
5 | récupération portable du retour à la ligne |
6 | lancement de toutes les fonctions débutant par 'test' |
7 | fonction (ré-)exécutée avant chaque fonction de test et qui exécute le programme à tester |
8 | lance le programme |
9 | se connecte à l’écran (sortie standard) du programme lancé |
10 | se connecte au clavier (entrée standard) du programme lancé |
11 | lit une ligne sur l’écran du programme lancé |
12 | écrit une ligne au clavier du programme lancé |
13 | force l’envoi de la ligne au clavier (vide le tampon de sortie) |
14 | lit une autre ligne sur l’écran du programme lancé |
5.3. Les tests implémentent des algorithmes simples
public void test_dates_invalides() {
int[][] tabJeuDEssaiDatesInvalides = { (1)
{1,1,1581},{0,1,2013},{99,99,2099},
{32,1,2013},{29,2,2013},{32,3,2013},
{31,4,2013},{32,5,2013},{31,6,2013},
{32,7,2013},{32,8,2013},{31,9,2013},
{32,10,2013},{31,11,2013},{32,12,2013},
{29,2,1900},{30,2,2000}
} ;
for ( int indice = 0, taille = tabJeuDEssaiDatesInvalides.length;
indice < taille ;
indice = indice + 1){
int[] date = tabJeuDEssaiDatesInvalides[indice] ;
assertFalse(date[0]+"/"+date[1]+"/"+date[2]+" est invalide"
, LibDate.dateValide(date[0],date[1],date[2])); (2) (3)
}
bilanAssertions = bilanAssertions + tabJeuDEssaiDatesInvalides.length ;
}
1 | given : dans les situations suivantes |
2 | when : quand on vérifie la validité de la date |
3 | then : on doit obtenir false |
5.4. Tout est-il testable ?
-
les librairies
-
les intéractions systèmes (concurrence, etc.)
-
les services réseau
-
les interfaces graphiques (html, java, flash, etc.)
-
…
-
PEUT-ETRE PAS, mais seulement après avoir essayé!
5.5. Toutes les manières de faire sont exploitables
|
6. Au menu du module M3301/MPA
Réaliser le développement d’une application logicielle en utilisant la méthode SCRUM
et qui se termine dans de bonnes conditions
6.1. Modalités
Chaque groupe dispose de ses force vives et de 2 TD + 2 TP par semaine :
-
pour réaliser le sprint courant (fournir tous les livrables)
Quizz
QUESTION
|