Test de performance avec Concordion

, , jeudi, février 11, 2010

Pour ceux qui ne connaissent pas Concordion, lisez d'abord cet article en français et surtout le site de Concordion.
En résumé, Concordion permet de rendre exécutable des documents (des spécifications logicielles ou un cahier des charges par exemple) afin de vérifier la correspondance entre le besoin exprimé et le code effectif du logiciel. On peut alors automatiser des tests de validation par exemple.
Dernièrement un test Concordion m'a posé quelques problèmes car il se mêlait de vérifier à la fois la réalisation d'une fonction mais aussi le temps de réponse de cette dernière, chose du genre:
"Après avoir choisi une ville dans la configuration, un graphique doit présenter la répartition des données en moins de 5 secondes."

En conséquence, ma fixture réalisait les préconditions ("avoir choisi une ville dans la configuration"), attendait 5 secondes puis vérifiait la présence d'un graphique.
Quand le test était OK, tout était pour le mieux mais dans le cas inverse, avec ce test, pas moyen de savoir si le graphique ne s'affichera jamais ou s'il se serait afficher 1 seconde ou 2 plus tard.

C'est à cause de ce genre de test (que l'on peut rapprocher des "tests de performance") que j'ai essayé de recencer ce qu'il était possible de réaliser avec Concordion.
La principale difficulté réside dans le fait que Concordion propose un nombre limité de commandes. On dispose de assertEquals et de assertTrue (ou assertFalse) mais pas de chose comme assertGreaterThan par exemple.
Tester qu'une fonction répond en moins de x secondes revient à réaliser l'opération de comparaison dans la fixture (via une méthode renvoyant un booléen) et à utiliser un assertTrue.

Du coup, si la fonction répond en plus de temps, le test est bien rouge mais n'indique que la valeur false (alors que l'on attend true).  (voir exemple 1 ci dessous)

On obtient un peu plus d'information en utilisant assertEquals comme le montre l'exemple 2 ci dessous.

Enfin, depuis la version 1.3.1RC5, on dispose d'une nouvelle commande "echo" qui étend nettement les solutions à cette problématique; car en plus de pouvoir tester que le temps de réponse est acceptable (assertTrue), on sait par ailleurs affiché la durée réelle d'éxecution; c'est l'exemple 3.



La classe à tester dans les exemples est la suivante:
package magicaltools;
import java.util.Random;

public class GoldenHammer {
    private static Random r = new Random();
 public void wait(int nms){
        long t0, t1;
        nms = nms + r.nextInt(150) - 75;
        t0 =  System.currentTimeMillis();
        do{
            t1 = System.currentTimeMillis();
        }
        while ((t1 - t0) < (nms));
    }
}
Pour accrocher ma spécification Concordion au code, j'utilise la fixture junit suivante :
package magicaltools;

import org.concordion.api.ExpectedToFail;
import org.concordion.integration.junit4.ConcordionRunner;
import org.junit.runner.RunWith;

@RunWith(ConcordionRunner.class)
@ExpectedToFail
public class GoldenHammerTest extends StopWatch{

 private GoldenHammer my = new GoldenHammer();
    
 public Boolean assertAnswerInLessThan(int approx_duration,int limit){
  start();
  my.wait(approx_duration);
  return stop()<limit*1000;
 }
 
 public String answerInLessThan2s(int approx_duration){
  start();
  my.wait(approx_duration);
  return (stop()<2000)?"moins de 2 secondes":""+realDuration()+" ms";
 }
 

 public String answerInLessThan2sAndExpect2fail(int approx_duration){
  return answerInLessThan2s(approx_duration);
 }
}
NB : la classe utilitaire Stopwatch possède l'implémentation suivante :
package magicaltools;

public class StopWatch {
 private long m_duration = 0;
 private long start =0L;
 public void start(){start = System.currentTimeMillis();}
 public long stop(){
  m_duration = System.currentTimeMillis()-start;
  return m_duration;
 }
 public long realDuration(){return m_duration;}
}

1 comments:

  1. Anonyme a dit…

    tres interessant, merci

Enregistrer un commentaire