I. Présentation de ContiPerf▲
Bonne nouvelle, cela existe et porte le nom de ContiPerf.
ContiPerf se présente comme une surcouche à Junit 4 pour les tests de performance, avec toutes ces facilités et tous ces avantages :
- configuration par annotations ;
- intégration avec les IDE supportant Junit 4 comme Eclipse, Netbeans… ;
- intégration avec Apache Maven ;
- aucune dépendance à part JUnit ;
- export des résultats au format CSV et HTML ;
- regroupement des tests en Test Suites.
Attention, ContiPerf ne remplace pas une bonne méthode de test de performance (avoir des résultats reproductibles, avoir les bons jeux de test…) et en particulier les tests de charge.
De plus, il n'est pas encore adapté au microbenchmarking.
Pour plus d'informations, le site officiel est très complet.
Avant de commencer à écrire des tests unitaires de performance, regardons comment intégrer ContiPerf à notre environnement de développement.
II. Intégration dans un IDE▲
Afin d'intégrer ContiPerf à votre IDE, il suffit d'ajouter la librairie contiperf.jar dans le Classpath.

Puis afin de lancer le test, on fait comme pour JUnit.
Par exemple avec Netbeans :
III. Intégration avec Maven▲
L'intégration à Maven est tout aussi simple.
2.
3.
4.
5.
6.
<dependency>
<groupId>org.junit </groupId>
<artifactId>com.springsource.org.junit</artifactId>
<version>4.7.0</version>
<scope>test</scope>
</dependency>
IV. Go pour la pratique▲
Maintenant que vous êtes entièrement convaincu (enfin j'espère !), regardons d'un peu plus près comment écrire un test de performance ContiPerf.
Pour ceux qui n'ont pas l'habitude d'utiliser JUnit, je leur conseille de l'étudier avant.
V. Écrire un test ContiPerf▲
Pour un test, il nous faut :
- un objet ContiPerfRule et son annotation @Rule ;
- un protocole de test (nombre d'itération, nombre de thread, durée du test) défini à l'aide de l'annotation @PerfTest ;
- des critères d'acceptance (moyenne / médian / max / percentiles / durée totale / throughput / temps de réponse) définis à l'aide de l'annotation @Required ;
- du code métier.
Le tout aura la forme suivante :
Si l'on veut que ContiPerf génère aussi les rapports, il faut remplacer la ligne :
2.
@Rule
public ContiPerfRule i = new ContiPerfRule();
par :
2.
3.
4.
5.
6.
@Rule
public ContiPerfRule rule = new ContiPerfRule(
new HtmlReportModule(),
new CSVSummaryReportModule(),
new CSVInvocationReportModule(),
new CSVLatencyReportModule());
VI. Exemple 1 : maximum 15 ms avec une moyenne inférieure à 10 ms▲
Nous allons écrire des tests ContiPerf pour l'application Spring PetClinic.
Dans un premier temps, nous allons tester la classe org.springframework.samples.petclinic.Pet
Testons la création (new Pet) de 100 chiens prénommés Medor (setName).
2.
3.
4.
5.
6.
7.
8.
9.
10.
@Rule
public ContiPerfRule i = new ContiPerfRule();
@Test
@PerfTest(invocations = 100, threads = 100)
@Required(max = 15, average = 10)
public void testsetName() {
Pet monChien = new Pet();
monChien.setName("Medor");
}
Dans ce test, on crée 100 chiens (invocations = 100) en parallèle (1 par thread) et on veut qu'une création dure au maximum 15 ms (max) avec une moyenne (average) inférieure à 10 ms.
VII. Exemple 2 : maximum 15 ms avec une moyenne inférieure à 10 ms▲
Reprenons le même exemple, mais cette fois-ci pour certaines raisons (par exemple un pool), on sait qu'il n'y aura jamais plus de 10 créations de chiens en même temps.
L'exemple devient :
2.
3.
4.
5.
6.
7.
8.
9.
10.
@Rule
public ContiPerfRule i = new ContiPerfRule();
@Test
@PerfTest(invocations = 100, threads = 10)
@Required(max = 15, average = 10)
public void testsetName() {
Pet monChien = new Pet();
monChien.setName("Medor");
}
Ici chaque thread va créer 10 chiens (invocations/threads) pour un total de 100 chiens.
VIII. Exemple 3 : throughput au minimum de 400▲
Toujours avec le même exemple, mais maintenant on veut que le throughput soit au minimum de 400 invocations par seconde.
2.
3.
4.
5.
6.
7.
8.
9.
10.
@Rule
public ContiPerfRule i = new ContiPerfRule();
@Test
@PerfTest(duration = 100000, threads = 10)
@Required(throughput = 400)
public void testsetName() {
Pet monChien = new Pet();
monChien.setName("Medor");
}
Ici le test dure 100 s.
IX. Exemple 4 : percentil▲
Enfin, pour ceux qui trouvent que l'utilisation du percentil est plus intéressante que la moyenne, il suffit de modifier le test de l'exemple 2 en remplaçant average par percentile99.
Par exemple, si on veut que 99 % des exécutions ne durent pas plus de 6 ms, l'exemple devient :
2.
3.
4.
5.
6.
7.
8.
9.
10.
@Rule
public ContiPerfRule i = new ContiPerfRule();
@Test
@PerfTest(invocations = 100, threads = 10)
@Required(max = 10, percentile99 = 6)
public void testsetName() {
Pet monChien = new Pet();
monChien.setName("Medor");
}
X. Exemple 5 : mutualisation des critères d'acceptance▲
Continuons avec la classe org.springframework.samples.petclinic.Pet.
Ajoutons le test de la fonction setType :
2.
3.
4.
5.
6.
7.
8.
9.
@Test
@PerfTest(invocations = 100, threads = 10)
@Required(max = 10, percentile99 = 5)
public void testsetType() {
PetType bulldog = new PetType();
bulldog.setName("bulldog");
Pet monChien = new Pet();
monChien.setType(bulldog);
}
Lorsqu'on a plusieurs tests pour une classe, on peut regrouper le protocole de test (@PerfTest) et les critères d'acceptance (@Required) au niveau de la classe et plus au niveau des fonctions.
Par exemple, pour mettre en commun notre protocole de test pour les deux tests ContiPerf, il suffit d'écrire le test de la manière suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
@PerfTest(invocations = 100, threads = 10)
public class PetTestsPerf {
@Rule
public ContiPerfRule i = new ContiPerfRule();
public PetTestsPerf() {
}
@Test
@Required(max = 10, percentile99 = 6)
public void testsetName() {
Pet monChien = new Pet();
monChien.setName("Medor");
}
@Test
@Required(max = 10, percentile99 = 5)
public void testsetType() {
PetType bulldog = new PetType();
bulldog.setName("bulldog");
Pet monChien = new Pet();
monChien.setType(bulldog);
}
}
XI. Exemple 6▲
Poursuivons avec un test plus complexe de la classe org.springframework.samples.petclinic.web.VisitsAtomView.
Pour cela, nous allons reprendre le test unitaire qui existe déjà et le transformer en test ContiPerf.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
package org.springframework.samples.petclinic.web;
import com.sun.syndication.feed.atom.Entry;
import com.sun.syndication.feed.atom.Feed;
import java.util.*;
import org.databene.contiperf.PerfTest;
import org.databene.contiperf.Required;
import org.junit.Before;
import org.junit.Test;
import org.springframework.samples.petclinic.Pet;
import org.springframework.samples.petclinic.PetType;
import org.springframework.samples.petclinic.Visit;
@PerfTest(invocations = 100, threads = 10)
public class VisitsAtomViewTestsPerf {
@Rule
public ContiPerfRule i = new ContiPerfRule();
private VisitsAtomView visitView;
private Map model;
private Feed feed;
@Before
public void setUp() {
visitView = new VisitsAtomView();
PetType dog = new PetType();
dog.setName("dog");
Pet bello = new Pet();
bello.setName("Bello");
bello.setType(dog);
Visit belloVisit = new Visit();
belloVisit.setPet(bello);
belloVisit.setDate(new Date(2009, 0, 1));
belloVisit.setDescription("Bello visit");
Pet wodan = new Pet();
wodan.setName("Wodan");
wodan.setType(dog);
Visit wodanVisit = new Visit();
wodanVisit.setPet(wodan);
wodanVisit.setDate(new Date(2009, 0, 2));
wodanVisit.setDescription("Wodan visit");
List visits = new ArrayList();
visits.add(belloVisit);
visits.add(wodanVisit);
model = new HashMap();
model.put("visits", visits);
feed = new Feed();
}
@Test
@Required(max = 30, percentile99 = 7)
public void buildFeedMetadata() {
visitView.buildFeedMetadata(model, feed, null);
}
@Test
@Required(max = 80, percentile99 = 42)
public void buildFeedEntries() throws Exception {
List entries = visitView.buildFeedEntries(model, null, null);
}
}
XII. Performance Test Suites▲
Bien sûr, on peut regrouper plusieurs tests ContiPerf dans une Test Suite.
Pour cela, il faut utiliser ces deux annotations :
@RunWith(ContiPerfSuiteRunner.class) ;
@Suite.SuiteClasses({liste des classes à tester})
Dans notre exemple on aura :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
package org.springframework.samples.petclinic;
import org.databene.contiperf.junit.ContiPerfSuiteRunner;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(ContiPerfSuiteRunner.class)
@Suite.SuiteClasses({org.springframework.samples.petclinic.PetTestsPerf.class, org.springframework.samples.petclinic.OwnerTestsPerf.class})
public class PetclinicTestSuite {
@BeforeClass
public static void setUpClass() throws Exception {
}
@AfterClass
public static void tearDownClass() throws Exception {
}
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
}
XIII. Rapports▲
Penchons-nous un peu plus sur les rapports générés par ContiPerf.
Avec la version 2.0.1, deux formats d'exports sont possibles.
- Le format HTML
Pour avoir ce rapport (fichier index.html), il faut utiliser public ContiPerfRule i = new ContiPerfRule(); ou public ContiPerfRule i = new ContiPerfRule(new HtmlReportModule());
- Le format CSV

Comme on peut le voir, on génère jusqu'à trois fichiers CSV.
summary.csv.
- Fichier contenant le résumé du test ContiPerf
2.
serviceId,startTime,duration,invocations,min,average,median,90%,95%,99%,max
org.springframework.samples.petclinic.OwnerTestsPerf.testHasPet,1329047753040,78,1000,0,0.8,0,0,0,24,6
Pour l'obtenir, il faut utiliser public ContiPerfRule i = new ContiPerfRule(new CSVSummaryReportModule());
org.springframework.samples.petclinic.OwnerTestsPerf.testHasPet.inv.csv
- Fichier contenant le temps de réponse de chaque itération
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
latency,startTimeNanos
67,9452032779934
63,9452036182461
0,9452100152044
0,9452100162800
0,9452100186266
0,9452100197511
0,9452100212177
0,9452100223492
62,9452037698296
62,9452037612671
64,9452035922372
67,9452033027032
59,9452040662290
59,9452040958767
59,9452041381935
61,9452038566633
Pour l'obtenir, il faut utiliser public ContiPerfRule i = new ContiPerfRule(new CSVInvocationReportModule());
org.springframework.samples.petclinic.OwnerTestsPerf.testHasPet.stat.csv
- Fichier contenant la répartition des temps de réponse
Par exemple, ici on voit qu'il y a eu une réponse avec un temps de réponse de 9 ms, neuf cent soixante-dix-neuf réponses avec un temps de réponse de 0 ms…
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
latency,sampleCount
0,979
1,0
2,2
3,0
4,2
5,0
6,0
7,0
8,0
9,1
10,0
11,0
Pour l'obtenir, il faut utiliser public ContiPerfRule i = new ContiPerfRule(new CSVLatencyReportModule());
Comme on peut le voir, on peut faire des choses intéressantes avec les rapports standards (surtout avec les fichiers CSV). Si cela n'est pas suffisant, ContiPerf étant open source et libre, on peut ajouter facilement enrichir des rapports et des métrics.
Supposons que nous voulions ajouter une metric dans le rapport HTML.
Pour cela, nous devons modifier/utiliser trois fichiers sources :
- HtmlReportModule : code générant le rapport HTML ;
- LatencyCounter : code calculant les metrics de base ;
- PerformanceRequirement : code contenant les critères d'acceptance.
Dans le cas de mon besoin initial, les metrics présentés suffisent largement. Cela dit, je reste curieux de vos remarques et des évolutions que vous aurez pu opérer en fonction des vôtres.
XIV. Conclusion ▲
On retient que ContiPerf est un utilitaire de test léger qui permet à l'utilisateur d'exploiter facilement JUnit 4 en faisant de tests de performance par exemple pour tester les performances en continu.













