Affichage des articles dont le libellé est junit. Afficher tous les articles
Affichage des articles dont le libellé est junit. Afficher tous les articles


Continuous Performance

Les test unitaires c'est bien, mais peut mieux faire!

J'ai déjà abordé le sujet de l'intégration continue avec un article sur Jenkins. Il est indéniable que ce genre d'outil apporte une sécurité et une assurance de stabilité dans la conception d'une application, même si cela ne fait pas tout!

Il est pourtant possible d'ajouter un nouvel élément dédié au respect des performances du code : les deux librairies JUnitPerf et ContiPerf sont des extensions JUnit qui permettent de mesurer notamment les temps d'exécution des différents tests.

JUnitPerf utilise essentiellement le design pattern Decorator pour ajouter des fonctionnalités aux tests existants. Assez simple d'utilisation, elle permet même de simuler des tests de monter en charge (Load test).

ContiPerf se base plutôt sur des annotations pour configurer les métriques désirées. Un peu plus souple et plus précis (par le calcul de moyennes sur des déciles), elle permet de générer des rapports graphiques sur les exécutions :

A vous de jouer!

Sources

  • http://blog.javabenchmark.org/2013/03/continuous-performance-and-junit-with.html

Fichier(s) joint(s) :



Xtext et JUnit

Le projet XText de la fondation Eclipse permet de facilement mettre en place des éditeurs de texte pour les langages dédiés (DSLs). Cependant, il est rapidement assez complexe de manipuler l'API de Xtext afin de l'intégrer à une batterie de tests unitaires.

Je vais donc ici fournir un extrait de code pour faciliter la prise en main et l'utilisation, depuis une application externe (JUnit), des moteurs de validation. L'intérêt est donc de pouvoir exécuter les contrôles de Xtext sur nos données et récupérer les éventuels erreurs et/ou warning remontés :

// On parse le texte extrait de l'éditeur
Injector injector = new MyStandaloneSetup().createInjectorAndDoEMFRegistration();
MyParser parser = injector.getInstance(MyParser.class);
InputStream  input = new ByteArrayInputStream(texte.getBytes());
IParseResult result = parser.doParse(input);

// On execute la validation Xtext
Diagnostic diagnostic = Diagnostician.INSTANCE.validate(parseResult.getRootNode().getElement());

// On récupère les erreurs/warning levés
boolean success = true;
String errorMessage = "Message(s) : \n";
switch (diagnostic.getSeverity()) {
case Diagnostic.ERROR:
 success = false;
 if (diagnostic.getException() != null) {
  diagnostic.getException().printStackTrace();
  errorMessage += diagnostic.getException().getMessage();
 } else if (diagnostic.getChildren().size() > 0) {
  for (Diagnostic d : diagnostic.getChildren()) {
   if (d.getException() != null) {
    d.getException().printStackTrace();
    errorMessage += d.getException().getMessage() + "\n";
   } else {
    errorMessage += d.getMessage() + "\n";
   }
  }
 } else {
  errorMessage += diagnostic.getMessage();
 }
 break;
case Diagnostic.WARNING:
 // No warning thrown
}

A partir de ces résultats, il est donc possible de contrôler simplement, avec les assertions JUnit, les retours des validations. Ce code s'inspire du paragraphe Validating Manually de la document XText.

De la même façon, il est possible de lancer l'exécution des switch qui parsent le contenu de l'éditeur afin de le traiter spécifiquement :

// Voir le parseResult instancié plus haut
MySwitch mySwitch = new MySwitch();
mySwitch.doSwitch(parseResult.getRootNode().getElement());

Enjoy!


Fichier(s) joint(s) :



JUnit et tests paramétrés - suite

Dans mon précédent article, j'évoquais le nouveau concept de tests paramétrés proposé par la dernière version du framework JUnit.

La principale "limitation" imposée par ce système réside dans l'architecture même de JUnit à ce niveau : le choix a été fait de rendre statique la méthode fournissant le jeu de données, ce qui est compréhensible puisque le moteur doit bien connaitre les données à tester avant de lancer l'exécution, mais cela implique également une certaine architecture des tests, parfois malhabile pour les maintenir et prévoir leur maintenance ou évolution (surtout quand les tests eux-mêmes doivent paramétrer les paramètres!).

Afin de contourner ce souci, JUnitExt propose une extension de JUnit contenant, parmi d'autres points, la possibilité de paramétrer les tests à partir d'une source de données XML, directement injectée au constructeur du test...

JUnitExt n'a plus l'air très maintenu depuis quelques temps et peut donc poser d'autres problèmes, mais sur ces quelques points il rempli très bien son office d'extension.

Je vous laisse le soin de parcourir le site officiel pour plus d'informations...

HTH!


Fichier(s) joint(s) :



JUnit et tests paramétrés

Dans la plupart des cas, l'écriture de tests JUnit se base sur plusieurs classes, contenant chacune des méthodes représentant les différents cas à tester. Ce concept permet l'organisation de tests visant à mettre à l'épreuve une série de fonctionnalités (métier ou techniques) dans différents scénarios. Or il est parfois nécessaire de réaliser le processus inverse : éprouver le même scénario plusieurs fois à partir d'un jeu de données précis...

Pour cela, le framework propose, dans sa dernière version, un nouveau runner permettant de réaliser ce genre d'action : les tests paramétrés. Voici le code à mettre en place :

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;

import junit.framework.TestCase;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class JUnitParameters extends TestCase {
 
 private File ctlFile;

 protected static Collection<Object[]> filesToTest = new ArrayList<Object[]>();
 
 @Parameters
 public static Collection<Object[]> getTestsFiles() throws Exception {
  return parseFolderForFilesToTest("C:\\JUnit");
 }
 
 protected static Collection<Object[]> parseFolderForFilesToTest(String folderPath) throws Exception {
  File folder = new File(folderPath);
  parseFolder(folder);
  return filesToTest;
 }

 private static void parseFolder(File folder) throws Exception {
  for (File f : folder.listFiles()) {
   if (f.isDirectory()) {
    parseFolder(f);
   } else {
    filesToTest.add(new Object[] { f });
   }
  }
 }
 
 /** Parameters inject by JUnit */
 public JUnitParameters(File file) {
  ctlFile = file;
 }
 
 @Test
 public void testGenerate() {
  assertTrue("Le fichier est vide!", ctlFile.exists() && ctlFile.length()>0);
 }

}

La première annotation @RunWith permet d'indiquer au runner qu'il s'agit d'un test paramétré. Il va donc avant toute action (avant même le @BeforeClass) appeler la méthode marquée par @Parameters afin de constituer le jeu de données. Chaque tableau d'objets est par la suite injecté au constructeur du test, rappelé à chaque jeu de données.

La suite du test s'exécute de manière classique, les méthodes marquées @Test sont appelées successivement par introspection.

Comme on peut le voir ci-dessous, ma classe ne contenant qu'une méthode de test a été exécutée autant de fois que nécessaire :


Fichier(s) joint(s) :