1. Avant-propos
Nous allons nous intéresser dans ce cours aux Méthodologies de la Production d'Applications.
Ce module, qui fait suite aux modules de :
-
programmation (M2103),
-
conception (M2104) et
-
d’IHM (M2105).
Il est fortement corrélé au module de conception avancé (M3105).
Nous suivons (comme tous les DUT informatique) le programme pédagogique national (PPN - disponible ici). |
2. Principes
Ce module s’intéresse aux Méthodes et outils de la Production d'Applications (MPA).
- Application
-
Dans notre cas, du logiciel.
- Production
-
Cela signifie que c’est le résultat d’une fabrication ⇒ build
- Méthodes
-
On illustrera certaines méthodes et on travaillera Scrum en particulier
- Outils
-
On continuera l’apprentissage des outils de base
3. Contenu et Progression
-
Cours SCRUM, Cycle -1 et Cycle 0, Tests
-
Cours Gestion des Modèles au cours du développement et à la livraison
-
Cours Intégration continue, documentation
-
Cours Modèles UML complémentaire (Activité, État)
-
Cours Maintenance, refactoring, évolutions
3.1. Organisation et emploi du temps
Au lieu de faire 2 modules en // (comme POO et COO l’an dernier), vous aurez ce semestre 2 fois plus d’heures, mais d’un seul module, l’un après l’autre (MPA puis CPOA).
# | Semaine | Cours |
---|---|---|
1 |
36 |
Introduction |
2 |
37 |
|
3 |
38 |
|
4 |
40 |
|
5 |
41 |
3.2. Projet fil rouge
-
TD1 : Comprendre l’objectif, planifier les tâches
-
TD2 : Développer à plusieurs et intégrer
-
TP1 : Développer seul et intégrer
-
TP2 : Développer à plusieurs et intégrer
-
TD3 : Evaluer le travail restant et les améliorations possibles de l’équipe, planifier les tâches
-
etc.
3.3. Développer une application java/web
-
Analyse
-
Conception ou évolution
-
Cycle de vie complet dont maintenance et exploitation
-
Normes logicielle/métriques qualité
-
Mise en production, documentation
4. Gestion de projet, Agilité, Tests
4.1. Gestion de projets
4.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
4.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)
4.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
4.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.
4.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.
4.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
4.2. Produire des applications
4.3. Méthodes
4.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/. |
4.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
|
4.3.3. Du cycle en V aux cycles en W
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
Bilan de l’exemple trivial
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
|
4.3.4. V versus W
V
|
W
|
4.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
4.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.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.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.)
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
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
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.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.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
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
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
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. |
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
4.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.
4.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
Axiomes
|
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 operations 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
-
4.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é |
4.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 |
4.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é!
4.5.5. Toutes les manières de faire sont exploitables
|
4.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.
Chaque groupe dispose de ses force vives et de 4 TD/TP par semaine pour réaliser le sprint courant (fournir tous les livrables).
5. Les principes de l'Agilité
Cette partie du cours est fortement inspirée par le MOOC Agile de Bertrand Meyer.
5.1. Le manifeste Agile
il date de février 2001! |
5.2. Les 17 auteurs
Les plus connus :
-
Ward Cunningham (Wiki)
-
Kent Beck (XP, JUnit)
-
Ken Schwaber et Jeff Sutherland (Scrum)
-
Alistair Cockburn
-
Martin Fowler
-
…
5.3. Les 12 principes
Vous trouverez une version actualisée des principes sur Wikipedia :
-
https://fr.wikipedia.org/wiki/Manifeste_agile#Les_12_principes
-
https://en.wikipedia.org/wiki/Agile_software_development
-
Customer satisfaction by early and continuous delivery of valuable software
-
Welcome changing requirements, even in late development
-
Working software is delivered frequently (weeks rather than months)
-
Close, daily cooperation between business people and developers
-
Projects are built around motivated individuals, who should be trusted
-
Face-to-face conversation is the best form of communication (co-location)
-
Working software is the principal measure of progress
-
Sustainable development, able to maintain a constant pace
-
Continuous attention to technical excellence and good design
-
Simplicity—the art of maximizing the amount of work not done—is essential
-
Best architectures, requirements, and designs emerge from self-organizing teams
-
Regularly, the team reflects on how to become more effective, and adjusts accordingly
-
Notre plus haute priorité est de satisfaire le client en livrant rapidement et régulièrement des fonctionnalités à grande valeur ajoutée.
-
Accueillez positivement les changements de besoins, même tard dans le projet.
-
Livrez fréquemment un logiciel opérationnel avec des cycles de quelques semaines à quelques mois et une préférence pour les plus courts.
-
Les utilisateurs ou leurs représentants et les développeurs doivent travailler ensemble quotidiennement tout au long du projet.
-
Réalisez les projets avec des personnes motivées. Fournissez-leur l’environnement et le soutien dont elles ont besoin et faites-leur confiance pour atteindre les objectifs fixés.
-
Privilégiez la co-location de toutes les personnes travaillant ensemble et le dialogue en face à face comme méthode de communication.
-
Un logiciel opérationnel est la principale mesure de progression d’un projet.
-
Les processus agiles encouragent un rythme de développement soutenable. Ensemble, les commanditaires, les développeurs et les utilisateurs devraient être capables de maintenir indéfiniment un rythme constant.
-
Une attention continue à l’excellence technique et à un bon design.
-
La simplicité – c’est-à-dire l’art de minimiser la quantité de travail inutile – est essentielle.
-
Les meilleures architectures, spécifications et conceptions émergent d’équipes auto-organisées.
-
À intervalles réguliers, l’équipe réfléchit aux moyens possibles pour devenir plus efficace. Puis elle s’adapte et modifie son mode de fonctionnement en conséquence.
-
5.4. Les valeurs agiles
Idées générales, qui précèdent aux principes.
Du manifesto lui-même :
-
Les individus et leurs interactions plus que les processus et les outils
-
Du logiciel qui fonctionne plus qu’une documentation exhaustive
-
La collaboration avec les clients plus que la négociation contractuelle
-
L’adaptation au changement plus que le suivi d’un plan
Ne pas oublier la petite phrase qui va avec : Nous reconnaissons la valeur des seconds éléments, mais privilégions les premiers.
— http://agilemanifesto.org/
|
Du MOOC Agile :
-
Nouveau rôle pour le manager (rôle réduit)
-
Pas d’étapes trop tôt ou trop importante (longues)
-
Développement itératifs (et donc continu)
-
Nouvelle façon de négocier (trade off)
-
Focus sur la qualité (et donc les tests)
5.5. Les principes
Plusieurs types :
-
Organisationnels / Managériaux
-
Techniques
5.5.1. Les bons principes NON AGILES!
-
Process, procédure et méthodes (critique ⇒ statique, imposée, ruptures entre phases)
-
Insister sur les exigences et leur qualités (critiques ⇒ ils évoluent, on ne les livrent pas, ils sont souvent des solutions plus que des besoins)
5.5.2. Les principes organisationnels
-
Le client au centre
-
Accepte le changement
-
Laisser l’équipe s’organiser
-
Maintenir un rythme durable
5.5.3. Les principes organisationnels (suite)
-
Produire du logiciel "minimaliste"
-
Les fonctionalités requises
-
et uniquement elles
-
ne développer que le code et les tests
-
YAGNI: You Ain’t Gonna Need It |
5.5.4. Les principes techniques
-
Développer itérativement
-
Mettre en avant les tests (TDD)
-
Non regression
-
Test first (TDD)
-
-
Exprimer les exigences comme des scénarios
-
User stories
-
5.6. Les rôles
-
Product Owner
-
SCRUM Master
-
Team
Mais où est passé le chef de projet?! |
Dans Scrum ⇒ pas de chef ! |
5.6.1. L’auto-organisation (dans l’équipe)
-
Spécialistes mais pas que
-
Transversalité : n’importe qui peut prendre n’importe quelle tâche
-
Sélection collective des objectifs pour l’itération
-
Affectation collective des tâches
-
Démonstration collective des résultats
5.6.2. Product Owner
-
Interface avec le client
-
Défini les caractéristiques du produit (features)
-
Priorise les features
-
Participe aux présentations du produit
5.6.3. Scrum master
-
S’assure que l’équipe applique correctement la méthode
-
S’assure que l’équipe est fonctionnelle
-
Facilite la coopération
-
Protège l’équipe
-
Aide à résoudre les problèmes et blocages
5.7. Les pratiques
-
Plannings
-
Meetings & Scrums
-
Retrospectives
-
TDD
5.7.1. Plannings
-
Planning poker
-
Scrum of Scrums
-
Storyboards
5.7.2. Meetings
-
Daily meetings
-
Matin généralement
-
"Stand-up" (<15')
-
Tout le monde participe
-
Engagements/Empêchements
-
-
Planning meetings
-
Sprint Backlog
-
-
Retrospective meetings
-
Qu’est-ce qui a marché?
-
Qu’est-ce qui peut être amélioré?
-
-
Review meetings
-
On implique le client
-
5.7.3. Focus sur le Daily meeting
Les 3 questions classiques :
-
Qu’as-tu fait hier?
-
Que vas-tu faire aujourd’hui?
-
Vois-tu des obstacles à venir?
5.7.4. Développement
-
Pair programming
-
Mentor
-
Une seule base de code (vs. branching)
-
Code partagé
-
Garder l’optimisation pour la fin
-
Conception simple
-
Conception incrémentale
-
Refactoring
5.7.5. Release
-
Tôt et souvent
-
Continuous Integration
-
Petite, Incrémentale
-
Cycles hebdomadaires
5.7.6. Tests
-
Utiliser les standards
-
Systématiser les Tests Unitaires
-
TDD
5.8. Les artefacts
-
Product Backlog
-
User stories
-
Sprint Backlog
-
Burdown
5.8.1. Product Backlog
-
Propriété du Product Owner
-
Maintenu et "vivant" tout au long du projet
-
Ouvert
-
Contiens des backlog items
-
Inclue des estimations des business values
-
Inclue des estimations des efforts de développement
5.8.2. User stories
- As a
-
…
- I want to
-
…
- So that
-
…
Bonne pratique (XP ⇒ INVEST):
-
Indépendante des autres histoires d’utilisateur (dans la mesure du possible)
-
Négociable (discutée avec l’équipe, notamment lors de l’estimation)
-
source de Valeur (porteuse d’une valeur client)
-
Estimable (elle peut être estimée par l’équipe)
-
(S)Courte (généralement une ou trois phrases environ)
-
D’une Taille appropriée (pouvoir être développée et testée au sein d’une itération)
Attributs :
-
Un numéro (Id)
-
Une importance/priorité client
-
Une estimation du coût (en points, temps, …)
-
Une expression de la forme "En tant que … je souhaite … pour pouvoir …
Une activité populaire consiste à organiser les Stories sous forme d’une matrice et non d’une simple liste : c’est le Story Mapping.
Must, Should, Could, Wont ⇒ MoSCoW
L’autre dimenstion de la matrice :
-
par flot (par dépendances entre stories)
5.8.3. Storyboard
5.8.4. Vélocité
Attention, ce n’est pas une vitesse! |
-
Number of items delivered
-
Burndown chart
5.8.5. Sprint Backlog
Juste un regroupement de User Stories, prisent dans le Product Backlog et traitées pour ce Sprint là.
5.8.6. Burndown
Pour tester vous-mêmes : https://docs.google.com/spreadsheets/d/17bppZL6IQoHXKHm0Cr5Q5yYrQivA9oTSB5fmo6ECsJs/edit#gid=6 |
6. Tests en intégration continue
J.-M. Bruel <jbruel@gmail.com> v1.0 2019-10-03 :imagesdir: images
6.1. Pourquoi tester ?
A majority of the production failures (77%) can be reproduced by a unit test.
Pour lire l’article en question : https://blog.acolyer.org/2016/10/06/simple-testing-can-prevent-most-critical-failures/amp/ |
6.1.1. Pour livrer le bon produit
6.1.2. Ce qui marche pour 1 ne marche pas nécessairement pour 100
6.1.3. La loi de Murphy
Tout ce qui est susceptible de mal tourner tournera nécessairement mal.
6.1.4. Différents OS ou différents terminaux
6.1.5. Pour donner le meilleur
6.2. Un exemple concret de test obligatoire
-
Fork the repository.
-
Run
bundle
to install development dependencies. -
Create a topic branch
-
Add tests for your unimplemented feature or bug fix. (See [writing-and-executing-tests])
-
Run
bundle exec rake
to run the tests. If your tests pass, return to step 4. -
Implement your feature or bug fix.
-
Run
bundle exec rake
to run the tests. If your tests fail, return to step 6. -
Add documentation for your feature or bug fix.
-
If your changes are not 100% documented, go back to step 8.
-
Add, commit, and push your changes.
-
Submit a pull request.
6.3. Un exemple concret de documentation obligatoire
[…] an Eclipse project is providing extensible frameworks and applications accessible via documented APIs.
6.4. Typologie des tests
Vérification |
Validation |
Le produit est-il bon ? |
Le produit est-il le bon ? |
Are you building it right? |
Are you building the right thing? |
Réalisée par le développeur |
Réalisée par le testeur |
En premier |
Après la vérification |
6.5. JUnit etc.
6.5.1. Quoi tester ?
Les exceptions |
|
Le temps d’exécution |
|
Uniquement certains environnement |
|
S’exécute avant les autres tests (e.g., accès à une base) |
|
6.5.2. Assertions
|
On force le test à échouer |
|
La condition est vraie |
|
La condition est fausse |
|
Les deux valeurs sont égales |
|
Objet nul |
|
Objets identiques (même réf.) |
6.5.3. Stratégie de tests
Considérons une fonction int add(int,int);
d’une classe myClass
.
Définir le comportement normal de la fonction (sortie normale pour des paramètres corrects).
//for normal addition
@Test
public void testAdd1Plus1() {
int x = 1 ; int y = 1;
assertEquals(2, myClass.add(x,y));
}
Ajouter des tests pour les cas particuliers :
-
aucune exception non capturée en cas d'overflow
-
les paramètres
null
sont gérés, e.g., :
//if you are using 0 as default for null, make sure your class works in that case.
@Test
public void testAdd1Plus1() {
int y = 1;
assertEquals(1, myClass.add(null,y));
}
6.5.4. L’ordre des tests
Surtout aucun!!
JUnit assumes that all test methods can be executed in an arbitrary order. Well-written test code should not assume any order, i.e., tests should not depend on other tests.
6.5.5. Sous Eclipse
-
Pour une classe existante : Right-click (dans le
Package Explorer
et ). -
Utiliser le
JUnit wizards
( ). -
Il n’y a plus qu’à faire
.
Pensez à utiliser le plug-in infinitest.
6.5.6. Et pour les interfaces graphiques?
Exemple de la librairie Robot
:
Robot bot = new Robot();
bot.mouseMove(10,10);
bot.mousePress(InputEvent.BUTTON1_MASK);
//add time between press and release or the input event system may
//not think it is a click
try{Thread.sleep(250);}catch(InterruptedException e){}
bot.mouseRelease(InputEvent.BUTTON1_MASK);
Exemple du plugin Eclipse swingcoder
:
6.5.7. Couverture des tests
Il existe des outils pour aller plus loin :
6.6. Application concrète pour ce cours
6.6.1. De To Be Done à On going
6.6.2. Créer une branche spécifique (si nouvelle feature)
bruel (master) $ git checkout -b US-15378
Switched to a new branch 'US-15378'
bruel (US-15378) $
6.6.3. Ecrire un test qui échoue
Etape 0 : Bien comprendre ce qu’on doit faire
Exemmple : Objectif de la tâche = créer une classe Pile
.
Rappels sur les propriétés d’une
Pile (opérations)
CréerPile : -> Pile estVide : Pile -> Booléen Empiler : Pile * Elément -> Pile Dépiler : Pile -> Pile Sommet : Pile -> Elément |
Rappels sur les propriétés d’une
Pile (préconditions)
Sommet(p) valide Si et Seulement Si estVide(p) == FAUX Dépiler(p) valide Si et Seulement Si estVide(p) == FAUX |
Rappels sur les propriétés d’une
Pile (axiomes)
(1) estVide(CréerPile()) (2) estVide(Empiler(p,e)) == FAUX (3) estVide(Dépiler(Empiler(p,e))) Si et Seulement Si estVide(p) (4) Sommet(Empiler(p,e)) == e (5) !estVide(p) => Sommet(Dépiler(Empiler(p,e))) == Sommet(p) |
Etape 1 : Ecrire un test simple
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class PileTest {
@Test
public void test_type_new_Pile() throws Exception {
Pile pile = new Pile() ;
assertEquals("Pile", pile.getClass().getName(),"new Pile() retourne une Pile");
}
}
Etape 2 : écrire un test qui passe
@Test
public void test_type_empiler() throws Exception {
Pile pile = new Pile() ;
assertEquals("Pile", pile.empiler("XXX").getClass().getName(),"empiler(pile,'XXX') retourne une Pile");
}
public class Pile {
public Object empiler(String string) {
// TODO Auto-generated method stub
return this;
}
}
La méthode générée par défaut retourne |
public class Pile {
public Pile empiler(String string) {
// TODO Auto-generated method stub
return new Pile();
}
}
Etape 2 : écrire un test qui échoue
@Test
public void test_axiome1() {
Pile pile = new Pile() ;
assertTrue(pile.estVide(pile),"Une nouvelle pile est vide");
}
public boolean estVide(Pile pile) {
// TODO Auto-generated method stub
return false;
}
Junit n’exécute que les fonctions qui commencent pas |
Etape 3 : On fait passer le test
public boolean estVide(Pile pile) {
// Smartly modified by JMB to pass the test!
return true;
}
Bien sûr le code n’est pas correcte pour l’instant (on s’en rendra compte dès les tests suivants)! Une meilleure solution pourrait être :
|
6.6.4. Essai de merge pour voir si tout le reste marche encore
bruel (US-15378) $ git commit -am "Adding push feature. Tests OK"
[US-15378 78f3242] Adding push feature. Tests OK
1 file changed, 2 insertions(+), 3 deletions(-)
bruel (US-15378) $ git checkout devs
Switched to branch 'devs'
bruel (devs) $ git merge US-15378
6.6.5. Commit & Push dans devs
bruel (devs) $ git commit -am "..."
...
bruel (devs) $ git push origin devs
...
bruel (devs) $ git branch -D US-15378
Deleted branch US-15378 (was f392a73).