1. Introduction

Retours d’expériences sur l'enseignement de l'Agilité en informatique du DUT jusqu’au Master.

1.1. Les formations évoquées dans ce retour d’expérience

1.1.1. L1/L2 - DUT Informatique - IUT de Blagnac

Public
Programme : les facettes du métier d’analyste programmeur

1.1.2. M1/M2 - Master Informatique ICE (ex NTIE) - Université Toulouse 2

Public
Programme : Master Informatique en alternance : Ingénierie Collaborative en Entreprise

2. Agiles sans le savoir

2.1. Ecrire un code de librairie

Warning

La fonction racine() de la librairie equation retourne :

  1. undef si les racines sont imaginaires

  2. un couple de valeurs identiques en cas de racine double

  3. un couple de valeurs si les racines sont multiples

Ecrire un code qui fasse passer les tests suivants avec succès

L1/L2
DUT Informatique
#============================================
# Fichier : equationTest.pl
# Role : Tests unitaires du module equation.pm
#============================================
use Test;
# Nombre de tests planifiés
BEGIN { plan tests => 6 }

use equation ;

print "# racine(1,2,3) == undef\n";
ok(equation::racine(1,2,3),undef);

print "# racine(1,4,4) == (x1,x1)\n";
my($x1,$x2) = equation::racine(1,4,4) ;
ok($x1,$x2);
print "# x1^2+4x1+4 == 0\n";
ok($x1*$x1+4*$x1+4,0);
...
#============================================
# Fichier : equationTest.pl
# Role : Tests unitaires du module equation.pm
#============================================
use Test;
# Nombre de tests planifiés
BEGIN { plan tests => 6 }

use equation ;

...

print "# racine(1,5,3) == (x1,x2)\n";
my($x1,$x2) = equation::racine(1,5,3) ;
ok($x1 != $x2);
print "# x1^2+5x1+3 == 0\n";
ok($x1*$x1+5*$x1+3,0);
print "# x2^2+5x2+3 == 0\n";
ok($x2*$x2+5*$x2+3,0);
Note

Le programme de test fourni est :

  • une documentation de spécification

    • précise et concise (et pour l’instant incomplète)

  • une documentation pour le programmeur

    • opérationnelle

Important
une solution
#============================================
# Fichier : equation.pm
# Role : Librairie de calcul de racines
#============================================
package equation ;

sub racine {
    my ($a,$b,$c) = @_ ;
    my $delta = $b*$b - 4*$a*$c ;
    if ( $delta >= 0 ) {
     my $x1 = (-$b + sqrt($delta))/2*$a ;
     my $x2 = (-$b - sqrt($delta))/2*$a ;
     return ($x1,$x2) ;
    }
    return undef ;
}

1;

2.2. Ecrire un code de test

Warning
Important
echo "# Configuration du serveur";
source site1-dev-user.sh

echo "# Lancement du serveur";
/etc/init.d/apache2 start

echo "# Tests d'accès à site1.ll.edu";
urls="
http://127.0.0.1/ http://127.0.0.1/test.html
http://127.0.0.1:80/ http://127.0.0.1:80/test.html
...
http://admin:boss@site1/dev/ http://machin:chose@site1/dev/
http://admin:boss@site1/user/ http://machin:chose@site1/user/"

for url in $urls
do
 if wget -O /dev/null -S -t 1 $url 2>&1 | grep -q 'HTTP/1.1 200 OK'
 then echo " OK : wget -S -t 1 $url 2>&1 | grep -q 'HTTP/1.1 200 OK'"
 else echo "NOK : wget -S -t 1 $url 2>&1 | grep -q 'HTTP/1.1 200 OK'"
 fi
done

echo "# Arrêt du serveur";
/etc/init.d/apache2 stop

3. Comprendre les tests

3.1. Tests unitaires

Les plus simples et pourtant il s’agit …

Warning

Généraliser le code de test de la fonction racine().

première proposition
my($a,$b,$c) = (-1,10,1);
while ($b < 20){
 print "#-----------------------------\n";
 print "# racine($a,$b,$c) == (x1,x2)\n";
 my($x1,$x2) = equation::racine($a,$b,$c) ;
 ok($a*$x1*$x1+$b*$x1+$c, 0)  ;
 ok($a*$x2*$x2+$b*$x2+$c, 0);
 $b += 2 ;
}
L1/L2
DUT Informatique
Que se passe-t-il donc ? Qui est le fautif ?
> perl equationTest.pl
...
#-----------------------------
# racine(-1,10,1) == (x1,x2)
not ok 8
# Test 8 got: "3.44169137633799e-015" (equationTest.pl at line 47)
#   Expected: "0"
#  equationTest.pl line 47 is:  ok($a*$x1*$x1+$b*$x1+$c , 0);
ok 9
#-----------------------------
...

3.1.1. Oublier les approches manuelles

my($a,$b,$c) = (-1,10,1);
while ($b < 20){
 print "#-----------------------------\n";
 print "# racine($a,$b,$c) == (x1,x2)\n";
 my($x1,$x2) = equation::racine($a,$b,$c) ;
 ok($a*$x1*$x1+$b*$x1+$c < .1E-14)  ;
 ok($a*$x2*$x2+$b*$x2+$c < .1E-14);
 $b += 2 ;
}

3.1.2. Expliciter les limites (3 secondes de test)

my($a,$b,$c) = (-1,10,1);
while ($b < 10000){
 print "#-----------------------------\n";
 print "# racine($a,$b,$c) == (x1,x2)\n";
 my($x1,$x2) = equation::racine($a,$b,$c) ;
 ok($a*$x1*$x1+$b*$x1+$c < .1E-6);
 ok($a*$x2*$x2+$b*$x2+$c < .1E-6);
 $b += 2 ;
}

3.1.3. Traiter le qualitatif et le quantitatif (27 secondes de test)

my($a,$b,$c) = (-1,10,1);
while ($b < 100000){
 print "#-----------------------------\n";
 print "# racine($a,$b,$c) == (x1,x2)\n";
 my($x1,$x2) = equation::racine($a,$b,$c) ;
 ok($a*$x1*$x1+$b*$x1+$c < .1E-4);
 ok($a*$x2*$x2+$b*$x2+$c < .1E-4);
 $b += 2 ;
}
Note
Ce qu’il faut retenir …
  • C’est irréalisable par un humain

  • Les limites sont aussi importantes que les cas standards pour l’utilisateur de la librairie

  • Le retour sur investissement est important : l’information fournie sur la fonction versus le volume du code de test

3.2. Tests d’intégration

Plus délicat, il s’agit …

Warning

Ecrire le code de test du programme racine.pl.

L1/L2
DUT Informatique
#============================================
# Fichier : racine.pl
# Role : Calcule les racines d'une équation du second degré
#============================================
use equation ;

if (($#ARGV != 2) || ($ARGV[0] == 0)) {
 print "Usage: $0 a b c\n" ;
 print "où : ax2 + bx + c = 0 et a != 0\n" ;
 exit(1);
}
($a,$b,$c) = @ARGV ;
print "${a}x2 + ${b}x + $c = 0\n";

my($x1,$x2) = equation::racine($a,$b,$c);

if (defined($x1)) {
 print "x1 = " . $x1 . "\n" ;
 print "x2 = " . $x2 . "\n" ;
}
else {
 print "Les racines de cette équation sont imaginaires\n";
}

3.2.1. Tester les exigences du client

print "# perl racine.pl -> affiche \"Usage: ...\"\n";
my $str = `perl racine.pl` ;
ok($str,'/Usage: racine.pl a b c\noù : ax2 \+ bx \+ c = 0 et a != 0\n/');

print "# perl racine.pl 1 2 3 -> affiche \"Les racines ...\"\n";
my $str = `perl racine.pl 1 2 3` ;
ok($str,'/Les racines de cette équation sont imaginaires/');

print "# perl racine.pl 1 -5 4 -> affiche \"x1 = 4 ...\"\n";
my $str = `perl racine.pl 1 -5 4` ;
ok($str,'/x1 = 4\nx2 = 1\n/');

3.2.2. Tester les intéractions système

print "# perl racine.pl -> code retour == 1\n";
$retour = system("perl racine.pl");
ok($retour >> 8,1);

print "# perl racine.pl 1 -> code retour == 1\n";
$retour = system("perl racine.pl 1");
ok($retour >> 8,1);

print "# perl racine.pl 1 1 -> code retour == 1\n";
$retour = system("perl racine.pl 1 1");
ok($retour >> 8,1);

print "# perl racine.pl 0 1 1 -> code retour == 1\n";
$retour = system("perl racine.pl 0 1 1");
ok($retour >> 8,1);

print "# perl racine.pl 1 2 3 -> code retour == 0\n";
$retour = system("perl racine.pl 1 2 3");
ok($retour >> 8,0);

3.3. Tout est-il testable ?

3.3.1. Tester une interface web de service

Warning

Tester le service de sondage suivant :

data/sondage.jpg
L2/L3
DUT/L3 Informatique
require './s4res_sondage'
require "webrick"
require "utils.rb"
require 'test/unit'

class S4RESSondageTest < Test::Unit::TestCase
  def test_Sondage
    # Service non lancé
    # Lancement du serveur

    3.times do
      # Lancement du service
      # Retour status("1.2.3.4")  et Retour status("127.0.0.1")
      # Erreur HTTP pour GET /
      # Réponse 200 OK et Contenu pour GET /sondage/
      # Réponse 200 OK pour GET /sondage/pub/sondage.ico
      # Réponse 304 Not Modified pour GET /sondage/pub/sondage.ico  Conditionnel
      # Votes depuis la même machine
      # Modifications paramètres
      # Arrêt du service
      # Retour status("1.2.3.4")  et Retour status("127.0.0.1")
      # Erreur HTTP pour GET /sondage
     end   #3.times

  end #test_Sondage
end
Test unitaire de la méthode status()
      # Retour status("1.2.3.4")
      assert_equal("      <div class=\"service\">
       <div>Service <span class=\"type\">sondage</span></div>
       <div>URL : <a href=\"/sondage\">/sondage</a></div>
       <div>Etat : on</div>
      </div>
",sondage.status("1.2.3.4"))

      # Retour status("127.0.0.1")
      assert_equal("      <div class=\"service\">
       <div>Service <span class=\"type\">sondage</span></div>
       <div>URL : <a href=\"/sondage\">/sondage</a></div>
       <div>Etat : on</div>
       <FORM method=\"POST\" >
         <INPUT type=\"hidden\" name=\"Arrêter\" value=\"/sondage\">
         <INPUT type=\"submit\" value=\"Arrêter\">
       </FORM>
      </div>
",sondage.status("127.0.0.1"))
Test unitaire du contenu de la page
      # Contenu page pour GET /sondage/
       re = %r|<html><head>
 <meta http-equiv='Content-Type' content='text/html'; charset=UTF-8'>
 <link rel='icon' type='image/x-icon' href='/sondage/pub/sondage.ico' />
 <title>L'agilité à quel age - Service Sondage - v1.0</title>
</head><body><center>
      <h1>L'Agilité à quel age <img src='/sondage/pub/sondage.ico'/></h1>
      <h3>Pensez-vous que l'Agilité devrait être enseignée en maternelle</h3>
      Je suis d'accord : 0 vote\(s\) <br/>
      Je ne suis pas d'accord : 0 vote\(s\) <br/>
      Je ne sais pas : 0 vote\(s\)
      <hr/>
...

 <hr/><center>Service Sondage <img src='/sondage/pub/sondage.ico'/> - v1.0</center><hr/>
</body></html>|m
      requete = Net::HTTP::Get.new("/sondage/")
      5.times {
       http.request(requete){|reponse| assert_match(re,reponse.body)}
      }
Test unitaire d’utilisation du service
      # Votes depuis la même machine
      votes = [["oui",[1,0,0]],["non",[0,1,0]],["bof",[0,0,1]]]
      requete = Net::HTTP::Get.new("/sondage/")
      (votes + votes).each {|vote,nbs|
        params = "reponse=#{vote}"
        reponse,body = http.post("/sondage/", params)
        assert_equal("302",reponse.code)
        re = %r|Je suis d'accord : #{nbs[0]} vote\(s\) <br/>
      Je ne suis pas d'accord : #{nbs[1]} vote\(s\) <br/>
      Je ne sais pas : #{nbs[2]} vote\(s\)|
        http.request(requete){|reponse| assert_match(re,reponse.body)}
       }
Test unitaire de modification des paramètres
      # Modifications paramètres
      admins = [["titre"," NouVEAu TiTRe","<h1> NouVEAu TiTRe"],
                ["question"," NouvELLe QuEsTiON ","<h3> NouvELLe QuEsTiON "],
                ["datefin","30",/Il vous reste 28[0-9.]+/]]

      (admins + admins).each {|param,val,re|
        params = "#{param}=#{val}"
        reponse,body = http.post("/sondage/", params)
        sleep(1)
        http.request(requete){|reponse| assert_match(re,reponse.body)}
      }
Warning

Tester le serveur générique suivant :

data/s4res.jpg
L2/L3
DUT/L3 Informatique

Une solution

3.3.2. Tests Quantitatifs

Warning

Combien de requêtes 304 Not Modified peut retourner votre serveur en 1 seconde ?

Combien de fois peut-on charger une page de 4Ko en 1 seconde ?

L2/L3
DUT/L3 Informatique
>ruby serveur0pTestNb.rb
 5 réponses '304 Not Modified' dans la même seconde
88 réponses '304 Not Modified' dans la même seconde
89 réponses '304 Not Modified' dans la même seconde

62 réponses '200 OK' (4Ko) dans la même seconde
69 réponses '200 OK' (4Ko) dans la même seconde
70 réponses '200 OK' (4Ko) dans la même seconde
Note
Ce qu’il faut retenir
  • Faire les tests à la main, c’est inhumain !

    • répétitif, fastidieux et imprécis

    • non réutilisable

3.4. Extreme Programming ou pas ?

Warning
Ecrire en premier lieu le programme de test
l’idée est séduisante
  • peu comprise et DIFFICILE à réaliser …

L1/L2
DUT Informatique
  • comprise mais ENCORE difficile à réaliser …

M1/M2
Master Informatique ICE
Note
Ce qu’il faut retenir …
  • Eviter XP avec un langage de programmation de tests non maitrisé

4. Comprendre le déroulement d’un projet

Warning
L/M
DUT Informatique/M ICE Informatique

4.1. Au commencement, il y a le cycle en V

Les cycles en V adoptent :

4.1.1. Les problèmes constatés du cycle en V

  1. Le cahier des charges est un contrat sur lequel il est couteux de revenir

  2. La phase d’intégration produisant l’architecture complète de l’application est tardive

  3. Plus le niveau d’abstraction augmente, plus la validation est éloignée dans le temps avec la définition des besoins

Note
Arrêt du projet
  • l’application n’est pas livrable

  • au mieux des modules/classes de base peuvent être réutilisés

Note
Dérive temporelle
  • la phase d’intégration est raccourcie

  • le produit livré a été trop peu testé

4.1.2. En résumé, les difficultés se situent :

4.2. Les cycles de développement Agile

Warning
L2/M
DUT Informatique/M ICE Informatique

data/amdd.jpg

4.2.1. Le cycle 0

Mise en place d’une architecture d’intégration système/réseau et logicielle.

Warning Tâche très difficile en L1/L2
Warning
Degré zéro du Cycle 0

Configurer 1 serveur de développement Debian lenny avec les services DHCP, DNS, SSH, AMP, SMTP, SVN et Redmine.

Fournir les scripts d’installation, de configuration et de tests.

M1
Master ICE Informatique

4.2.2. Les itérations suivantes

Courtes, testées, documentées, refactorisées …

Warning
Cycles 1, 2, …

Fournir les scripts de reconfiguration et de tests assurant une sécurisation des services.

M1
Master ICE Informatique

5. Agilité et gestion de projet

Les résultats attendus d’un développement Agile sont :

  1. une application qui répond aux attentes du client

  2. une application robuste, maintenable et évolutive

Warning

Découvrir, analyser et poursuivre un projet issu d’un développement Agile

M2
Master ICE Informatique

5.1. Découverte et analyse

Note
Ce qu’il faut retenir
  • La prise en main d’un projet est rapide

  • Il est normal que la taille des codes de tests soit au moins égale à la taille du code applicatif

  • La tâche de maintenance des codes (tests + applicatifs) n’est pas proportionnelle à leur volume.

5.2. La documentation des projets Agiles

Note Ni trop, ni trop peu
Warning
La documentation dont rève le chef de projet

Fournir automatiquement un diagramme des classes testées.

M2
Master ICE Informatique
data/s4resdotclass.gif

6. Complexités, confiances et Agilités

Compléxité

Combinatoire des possibles d’un système

Combinatoire des états possibles d'un système logiciel
Combinatoire des états possibles d'une équipe de projet
Confiance

Processus statistique de réduction de la compléxité

DO; ... DONE;
Agilité
  • l'équilibre Programme de Test — Programme Testé

    • apporte de la confiance au programmeur qui l’a construit

    • apporte de la confiance au programmeur qui l’utilise

    • apporte de la confiance au chef de projet et au client

    • facilite la répartition des tâches, l’arrêt, la reprise et l’intégration dans un projet

  • l’intégration du client, l’organisation des équipes

    • accroit la confiance réciproque

  • Confiances nécessaires pour gérer la Complexité tant logicielle et matérielle, qu’humaine.

7. Bilan

A l’initiative d'Henry Massié : Modalités d’enseignement de l’Agilité dans certaines formations Toulousaines

8. Postscriptum

8.1. L'édition Toulousaine de la Nuit de l’info 2009 aura lieu à l’IUT de Blagnac

http://nuitinfo09.polytech.unice.fr

La Nuit de l’info 2009 aura lieu les 3 et 4 décembre 2009, de 16h39 à 08h02. A l’image des éditions précédentes, elle réunira des étudiants de toute la France, pour une grande aventure collective, pour un temps de fête, pour un mélange d’informatique, de communication, de marketing, d'extrême programming, de modélisation, de pizzas, de café, de glaces, de musique, de films…