I. Configuration logicielle▲
Les logiciels utilisés dans ce tutoriel sont les suivants :
- Eclipse Mars ;
- Karaf 4.0.4 ;
- JDK 1.7.
Les APIApplication Programming Interface Java utilisées sont :
Les frameworks utilisés :
- Hibernate ;
- Apache CXF ;
- Pax Exam 4.7.0.
II. OSGI et les tests unitaires▲
Comme pour toute application, vous pouvez réaliser des tests unitaires sur les bundles OSGI que nous venons de développer.
Ces tests unitaires sont facilités par l'utilisation du framework « Pax Exam » dont vous pouvez consulter la page officielle à cette adresse : https://ops4j1.jira.com/wiki/display/PAXEXAM4/Pax+Exam
Les avantages de « Pax Exam » sont nombreux et se résument à ceci :
- contrôler facilement le framework OSGI, le framework de test (JUnit dans notre cas, mais aussi TestNG) et les bundles ;
- démarrer un serveur Karaf pour y exécuter des commandes, déployer des features… ;
-
exécuter facilement des tests OSGI par ajout d'annotations spéciales et d'une méthode de configuration à une classe Junit.
« Pax Exam » se compose des éléments suivants :
-
un « Test Driver » qui lance le framework OSGI et les bundles à tester. Le rôle de cet élément est de construire des bundles à la volée à partir de nos cas de tests et de les injecter dans le container (Karaf) ;
-
un « Test Container » composé de deux parties :
- un container natif (« Native Container ») qui lance le framework OSGI embarqué à Karaf dans la machine virtuelle du « test driver » ;
- un « Forked Container » qui permet d'exécuter les bundles dans une machine virtuelle séparée et contrôlée par le « Test Driver ».
Le « Test Container » supporte différentes stratégies pour redémarrer ou réutiliser le framework OSGI en cours d'exécution pour chaque test.
III. Création du module de test▲
-
Cliquez droit sur le projet « customerRestFulHibernateWS » → « New » → « Project ». Dans la fenêtre qui apparaît, saisissez « maven » dans le filtre puis choisissez « Maven module ».
Cliquez sur le bouton « Next ».
-
Dans la fenêtre qui apparaît, ne cochez pas la case « Create a simple project (skip archetype selection) ».
Saisissez le nom de notre module de test. Par exemple : « module-test ».Cliquez sur le bouton « Next ».
-
Dans le champ « Filter » de la fenêtre qui apparaît, saisissez « pax » puis, dans la liste qui apparaît, choisissez la ligne correspondant à « exam-karaf-archetype » puis cliquez sur le bouton « Next ».
-
Renseignez la fenêtre qui apparaît de cette manière :
Group Id : com.exemple.customerRestFulHibernateWS
Artefact Id : module-test
Version : 1.0.0
Package : com.exemple.customerRestFulHibernateWS.testCliquez sur le bouton « Finish ».
-
Dans l'explorateur de projets d'Eclipse, nous pouvons voir que notre projet « customerRestFulHibernateWS » a été complété par le module « module-test » :
Le fichier « pom.xml » a été modifié pour y inclure le nouveau module (« module-test ») :
On peut également voir qu'un nouveau projet nommé « module-test » a été créé :
-
Renommez le fichier « /src/test/java/com/exemple/customerRestFulHibernateWS/test/MyTest.java » en « /src/test/java/com/exemple/customerRestFulHibernateWS/test/CustomerDaoTest.java » en cliquant droit dessus puis en sélectionnant « Rename… ».
- Renommez également la classe à l'intérieur de ce fichier si ce n'est pas fait automatiquement. Le nom de la classe passera de MyTest à CustomerDaoTestcomme le présente la capture d'écran ci-dessous :
Il s'agira de notre classe de test pour la classe CustomerDao.
IV. Configuration de Pax Exam▲
Dans le projet « module-test », ouvrez le programme « /src/test/java/com/exemple/customerRestFulHibernateWS/test/CustomerDaoTest.java ».
Nous pouvons lire les lignes suivantes :
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.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
package
com.exemple.customerRestFulHibernateWS.test;
import
static
org.hamcrest.CoreMatchers.is;
import
static
org.hamcrest.CoreMatchers.notNullValue;
import
static
org.junit.Assert.assertThat;
import
static
org.ops4j.pax.exam.CoreOptions.maven;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.configureConsole;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.features;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.logLevel;
import
java.io.File;
import
javax.inject.Inject;
import
org.apache.karaf.features.BootFinished;
import
org.apache.karaf.features.FeaturesService;
import
org.junit.Test;
import
org.junit.runner.RunWith;
import
org.ops4j.pax.exam.Configuration;
import
org.ops4j.pax.exam.Option;
import
org.ops4j.pax.exam.junit.PaxExam;
import
org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel;
import
org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import
org.ops4j.pax.exam.spi.reactors.PerClass;
import
org.osgi.framework.BundleContext;
@RunWith
(
PaxExam.class
) //Permet de lier Pax Exam a JUnit afin de mettre en place un container de test (Karaf) contenant un framework OSGI et nos bundles a tester
@ExamReactorStrategy
(
PerClass.class
) //Determine si le framework OSGI doit redemarrer ou pas pour chaque test.
public
class
CustomerDaoTest {
@Inject
private
BundleContext bc;
@Inject
protected
FeaturesService featuresService;
/**
* To make sure the tests run only when the boot features are fully
* installed
*/
@Inject
BootFinished bootFinished;
@Configuration
public
static
Option[] configuration
(
) throws
Exception
{
return
new
Option[]
{
karafDistributionConfiguration
(
).frameworkUrl
(
maven
(
).groupId
(
"org.apache.karaf"
).artifactId
(
"apache-karaf"
)
.type
(
"zip"
).version
(
"3.0.3"
))
.unpackDirectory
(
new
File
(
"target/paxexam/unpack/"
))
.useDeployFolder
(
false
),
configureConsole
(
).ignoreLocalConsole
(
),
logLevel
(
LogLevel.INFO),
keepRuntimeFolder
(
),
features
(
maven
(
).groupId
(
"org.apache.karaf.features"
)
.artifactId
(
"standard"
).type
(
"xml"
)
.classifier
(
"features"
).version
(
"3.0.3"
),
"eventadmin"
)
}
;
}
@Test
public
void
shouldHaveBundleContext
(
)
{
assertThat
(
bc, is
(
notNullValue
(
)));
}
@Test
public
void
checkEventFeature
(
) throws
Exception
{
assertThat
(
featuresService.isInstalled
(
featuresService.getFeature
(
"eventadmin"
)), is
(
true
));
}
}
Il s'agit du code Java créé par défaut lors de la création de notre sous-module. Ce code permet d'installer un container Karaf et de le configurer.
Or, au cours de nos tests unitaires, nous aurons certainement besoin de créer plusieurs classes de test et par conséquent, le code permettant d'installer et de paramétrer notre serveur Karaf sera réutilisé.
Il est par conséquent judicieux de le placer dans une classe abstraite afin d'éviter d'écrire plusieurs fois le même code et de centraliser la version du container en cas d'évolution. Pour cela :
-
Créez une nouvelle classe nommée KarafContainer en cliquant droit sur le package « com.exemple.customerRestFulHibernateWS.test » puis, dans le menu contextuel qui apparaît, choisissez « New » → « Class » :
-
Dans la section « Name », saisissez « KarafContainer » et cochez la case « abstract » :
Cliquez sur « Finish ». Le fichier « KarafContainer.java » s'ouvre alors.
- Coupez-collez le contenu de la méthode
configuration
(
)dans la classe KarafContainer. Copiez également les annotations « @RunWith » et « @ExamReactorStrategy ». La classe KarafContainer ressemble donc à cela :
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.
package
com.exemple.customerRestFulHibernateWS.test;
import
static
org.hamcrest.CoreMatchers.is;
import
static
org.hamcrest.CoreMatchers.notNullValue;
import
static
org.junit.Assert.assertThat;
import
static
org.ops4j.pax.exam.CoreOptions.maven;
import
static
org.ops4j.pax.exam.CoreOptions.mavenBundle;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.configureConsole;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.features;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.logLevel;
import
java.io.File;
import
javax.inject.Inject;
import
org.apache.karaf.features.BootFinished;
import
org.apache.karaf.features.FeaturesService;
import
org.junit.Test;
import
org.ops4j.pax.exam.Configuration;
import
org.ops4j.pax.exam.Option;
import
org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel;
import
org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import
org.ops4j.pax.exam.spi.reactors.PerMethod;
import
org.osgi.framework.BundleContext;
@RunWith
(
PaxExam.class
) //Permet de lier Pax Exam a JUnit afin de mettre en place un container de test (Karaf) contenant un framework OSGI et nos bundles a tester
@ExamReactorStrategy
(
PerClass.class
) //Determine si le framework OSGI doit redemarrer ou pas pour chaque test.
public
abstract
class
KarafContainer
{
@Configuration
public
static
Option[] configuration
(
) throws
Exception
{
return
new
Option[]
{
karafDistributionConfiguration
(
).frameworkUrl
(
maven
(
).groupId
(
"org.apache.karaf"
).artifactId
(
"apache-karaf"
)
.type
(
"zip"
).version
(
"3.0.3"
))
.unpackDirectory
(
new
File
(
"target/paxexam/unpack/"
))
.useDeployFolder
(
false
),
configureConsole
(
).ignoreLocalConsole
(
),
logLevel
(
LogLevel.INFO),
keepRuntimeFolder
(
),
features
(
maven
(
).groupId
(
"org.apache.karaf.features"
)
.artifactId
(
"standard"
).type
(
"xml"
)
.classifier
(
"features"
).version
(
"3.0.3"
),
"eventadmin"
)
}
;
}
}
Dans les paragraphes suivants, nous présenterons comment configurer « Pax Exam » et par conséquent comment modifier ces lignes de manière à ce que ce framework réponde parfaitement à vos besoins.
IV-A. Mise en place de la stratégie de test▲
En premier lieu, nous remarquons, lignes 29 et 30 du fichier « /src/test/java/com/exemple/customerRestFulHibernateWS/test/KarafContainer.java », les annotations « @RunWith » et « @ExamReactorStrategy ».
L'annotation @RunWith
(
PaxExam.class
)permet de lier le framework « Pax Exam » à JUnit pour que « Pax Exam » installe, par la suite, un container de test (dans notre cas, il s'agit de Karaf) avec un framework OSGI contenant nos bundles de test.
L'annotation @ExamReactorStrategy
(
PerClass.class
)détermine si le framework OSGI doit redémarrer ou pas après chaque test. Elle détermine donc la stratégie de réaction de « Pax Exam ».
Comme vous pouvez le constater, dans notre cas, la stratégie utilisée est « PerClass ». Les stratégies possibles sont les suivantes :
-
PerMethod
Avec cette stratégie, le container de test (Karaf) est démarré avant chaque méthode de test individuel et arrêté juste après.
Il s'agit de la stratégie par défaut (si la stratégie n'est pas renseignée) ;
-
PerClass
Avec cette stratégie, le container de test est démarré et arrêté avant et après que tous les tests sur une classe de test donnée sont terminés. Par conséquent, les méthodes de tests suivantes ne sont pas complètement isolées des effets de bord des méthodes précédemment exécutées sur la même classe de test ;
- PerSuite
Avec cette stratégie, le container de test est démarré une seule fois avant l'exécution de toutes les classes de test et arrêté après que tous les tests ont été effectués. Cette stratégie minimise le temps de démarrage et d'arrêt du container au détriment de l'isolation et des effets de bord engendrés par les classes de tests ou les méthodes.
Remarquez que, dans notre classe KarafContainer, c'est la stratégie « PerClass » qui est renseignée par défaut lors de la création de la classe. Cette stratégie nous convient étant donné les tests unitaires que nous ferons du fait que très peu d'effets de bord sont à redouter entre les méthodes. Il faudra néanmoins accorder une attention particulière au fait que tout enregistrement inséré dans une table via une méthode de test devra être supprimé à la fin de l'exécution de cette méthode.
IV-B. Configuration du container de test▲
Tout test unitaire réalisé par « Pax Exam » requiert une méthode utilisant l'annotation « @configuration » et retournant un tableau d'options de configuration. Ces options définissent l'ensemble des bundles utilisés par le framework OSGI dans le container de test.
Dans notre cas, cette méthode se situe dans la classe KarafContainer et est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
@Configuration
public
static
Option[] configuration
(
) throws
Exception
{
return
new
Option[]
{
karafDistributionConfiguration
(
).frameworkUrl
(
maven
(
).groupId
(
"org.apache.karaf"
).artifactId
(
"apache-karaf"
)
.type
(
"zip"
).version
(
"3.0.3"
))
.unpackDirectory
(
new
File
(
"target/paxexam/unpack/"
))
.useDeployFolder
(
false
),
configureConsole
(
).ignoreLocalConsole
(
),
logLevel
(
LogLevel.INFO),
keepRuntimeFolder
(
),
features
(
maven
(
).groupId
(
"org.apache.karaf.features"
)
.artifactId
(
"standard"
).type
(
"xml"
)
.classifier
(
"features"
).version
(
"3.0.3"
),
"eventadmin"
)
}
;
}
Nous remarquons en premier, de la ligne 52 à la ligne 53 la configuration de l'installation de Karaf.
Ces lignes permettent en effet au framework « Pax Exam » d'installer le container de test. Nous constatons que, par défaut, il installera la version « 3.0.3 » de Karaf.
Étant donné que nous avons choisi Karaf en version « 4.0.4 enterprise » lors de la réalisation de notre projet, nous vous suggérons de modifier cette version « 3.0.3 » en « 4.0.4 enterprise ».
Ainsi, le code devient le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
karafDistributionConfiguration
(
).frameworkUrl
(
maven
(
).groupId
(
"org.apache.karaf"
).artifactId
(
"apache-karaf"
)
.type
(
"zip"
).version
(
"4.0.4"
))
.unpackDirectory
(
new
File
(
"target/paxexam/unpack/"
))
.useDeployFolder
(
false
),
configureConsole
(
).ignoreLocalConsole
(
),
logLevel
(
LogLevel.INFO),
keepRuntimeFolder
(
),
features
(
maven
(
).groupId
(
"org.apache.karaf.features"
)
.artifactId
(
"enterprise"
).type
(
"xml"
)
.classifier
(
"features"
).version
(
"4.0.4"
),
"eventadmin"
)
En plus des lignes concernant la configuration de notre container de test, nous pouvons ajouter plusieurs options de configuration possibles. Parmi elles, notons les plus importantes nous concernant :
- features()
Permet de fournir au container les features à installer. Il s'agit en fait des features que nous avons renseignées dans le fichier « module-kar/feature.xml » du tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf » ; - replaceConfigurationFile()
Permet de fournir ou de remplacer un fichier de configuration dans le répertoire « /etc » de Karaf. Cette option est utile, comme nous le verrons plus loin, pour installer notre DataSource ; - editConfigurationFilePut()
Permet de remplacer ou d'ajouter une option dans un fichier de configuration de Karaf. Cette option de configuration nous permettra de renseigner notre repository local Maven afin que Karaf aille récupérer les bundles constituant notre service web dans le repository local, là où ils ont été déposés par Eclipse ; - mavenBundle()
Permet de préciser l'URL Maven d'un bundle placé dans un repository local ou distant. Nous nous en servirons, par exemple, pour récupérer le driver PostgreSql nécessaire à notre application ; - junitBundles()
Permet de fournir au container une version par défaut de JUnit et de ses dépendances en tant que bundles OSGI.
La liste des options de configuration est disponible à cette adresse : https://ops4j1.jira.com/wiki/display/PAXEXAM4/Configuration+Options#ConfigurationOptions-OptionOverview
Afin de prévoir la mise en place de la Data Source Factory et de la Data Source, copiez le fichier « src/main/resources/etc/org.ops4j.datasource-customerDb.cfg » disponible dans le sous-module « module-karaf-custom-distribution » dans le répertoire « src/test/resources/ » de notre sous-module « module-test ».
À l'aide de ces informations, nous pouvons compléter la méthode configuration
(
) de notre classe KarafContainer 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.
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.
@Configuration
public
static
Option[] configuration
(
) throws
Exception
{
return
new
Option[]
{
karafDistributionConfiguration
(
).frameworkUrl
(
maven
(
).groupId
(
"org.apache.karaf"
).artifactId
(
"apache-karaf"
)
.type
(
"zip"
).version
(
"4.0.4"
))
.unpackDirectory
(
new
File
(
"target/paxexam/unpack/"
))
.useDeployFolder
(
false
),
configureConsole
(
).ignoreLocalConsole
(
),
logLevel
(
LogLevel.INFO),
keepRuntimeFolder
(
),
//Installation des features preinstallees dans Karaf
features
(
maven
(
).groupId
(
"org.apache.karaf.features"
).artifactId
(
"enterprise"
).type
(
"xml"
).classifier
(
"features"
).version
(
"4.0.4"
),"jdbc"
),
features
(
maven
(
).groupId
(
"org.apache.karaf.features"
).artifactId
(
"enterprise"
).type
(
"xml"
).classifier
(
"features"
).version
(
"4.0.4"
),"jpa"
),
features
(
maven
(
).groupId
(
"org.apache.karaf.features"
).artifactId
(
"enterprise"
).type
(
"xml"
).classifier
(
"features"
).version
(
"4.0.4"
),"transaction"
),
features
(
maven
(
).groupId
(
"org.apache.karaf.features"
).artifactId
(
"enterprise"
).type
(
"xml"
).classifier
(
"features"
).version
(
"4.0.4"
),"jndi"
),
//Installation des features supplementaires
features
(
maven
(
).groupId
(
"org.apache.cxf.karaf"
).artifactId
(
"apache-cxf"
).type
(
"xml"
).classifier
(
"features"
).version
(
"3.1.5"
),"cxf"
),
features
(
maven
(
).groupId
(
"org.apache.karaf.features"
).artifactId
(
"enterprise"
).type
(
"xml"
).classifier
(
"features"
).version
(
"4.0.4"
),"pax-jdbc"
),
features
(
maven
(
).groupId
(
"org.apache.karaf.features"
).artifactId
(
"enterprise"
).type
(
"xml"
).classifier
(
"features"
).version
(
"4.0.4"
),"pax-jdbc-config"
),
features
(
maven
(
).groupId
(
"org.apache.karaf.features"
).artifactId
(
"enterprise"
).type
(
"xml"
).classifier
(
"features"
).version
(
"4.0.4"
),"pax-jdbc-pool-dbcp2"
),
features
(
maven
(
).groupId
(
"org.hibernate"
).artifactId
(
"hibernate-osgi"
).type
(
"xml"
).classifier
(
"karaf"
).version
(
"5.1.0.Final"
),"hibernate-orm"
),
//Configuration du fichier "etc/org.ops4j.pax.url.mvn.cfg" pour renseigner notre repository local (derniere ligne)
editConfigurationFilePut
(
"etc/org.ops4j.pax.url.mvn.cfg"
, "org.ops4j.pax.url.mvn.repositories"
, "http://repo1.maven.org/maven2@id=central, "
+
"http://repository.springsource.com/maven/bundles/release@id=spring.ebr.release, "
+
"http://repository.springsource.com/maven/bundles/external@id=spring.ebr.external, "
+
"http://zodiac.springsource.com/maven/bundles/release@id=gemini, "
+
"http://repository.apache.org/content/groups/snapshots-group@id=apache@snapshots@noreleases, "
+
"https://oss.sonatype.org/content/repositories/snapshots@id=sonatype.snapshots.deploy@snapshots@noreleases, "
+
"https://oss.sonatype.org/content/repositories/ops4j-snapshots@id=ops4j.sonatype.snapshots.deploy@snapshots@noreleases, "
+
"http://repository.springsource.com/maven/bundles/external@id=spring-ebr-repository@snapshots@noreleases, "
+
"file://C:/Users/orozier/.m2/repository@id=1@snapshots"
),
//Installation du driver PostgreSql
mavenBundle
(
).groupId
(
"org.postgresql"
).artifactId
(
"postgresql"
).version
(
"9.4-1203-jdbc41"
),
//Creation de la Datasource
replaceConfigurationFile
(
"etc/org.ops4j.datasource-customerDb.cfg"
, new
File
(
"src/test/resources/org.ops4j.datasource-customerDb.cfg"
)),
//Installation des bundles JUnit
junitBundles
(
),
//Installation de nos bundles
mavenBundle
(
).groupId
(
"com.exemple.customerRestFulHibernateWS"
).artifactId
(
"module-data"
).version
(
"1.0.0"
),
mavenBundle
(
).groupId
(
"com.exemple.customerRestFulHibernateWS"
).artifactId
(
"module-services"
).version
(
"1.0.0"
),
mavenBundle
(
).groupId
(
"com.exemple.customerRestFulHibernateWS"
).artifactId
(
"module-jaxrs"
).version
(
"1.0.0"
)
}
;
}
Il est intéressant de savoir qu'en utilisant « Pax Exam » pour générer vos tests unitaires, vous pouvez non seulement importer une distribution officielle de Karaf en guise de container (comme nous l'avons vu ci-dessus), mais vous pouvez également importer une distribution personnalisée comme celle que nous avons réalisée dans notre tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf ». C'est d'ailleurs sur les sources de ce projet que nous basons le présent tutoriel.
Par conséquent, autant utiliser notre version personnalisée pour effectuer les tests unitaires plutôt qu'une version de container dans laquelle il nous faudrait tout installer (comme ci-dessus), tout en ayant des doutes sur la similitude entre cette nouvelle plateforme et notre distribution personnalisée.
Dès lors, vous voyez que, dans un même projet, il est possible de :
- organiser votre projet dans Eclipse en modules représentatifs des couches applicatives comme nous l'avons fait dans le tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf » ;
- transformer ces modules en autant de bundles OSGI ;
- générer une distribution personnalisée de Karaf embarquant, en natif, les bundles développés ;
- utiliser cette version personnalisée pour effectuer nos tests unitaires.
L'ensemble de ces tâches peut se faire que par un seul build (ce qui ne vous dispense pas de faire des tests unitaires au fur et à mesure de la création de vos classes).
L'avantage majeur est que, à l'issue du développement, non seulement les tests unitaires sont effectués, mais ils le sont sur une distribution de Karaf dans laquelle tous les développements sont déjà intégrés.
La phase d'intégration des bundles dans Karaf (avec tout ce que cela implique : installation, tests…) n'est donc plus du tout chronophage puisqu'elle est réalisée durant la phase de développement.
Pour utiliser notre distribution personnalisée de Karaf pour effectuer les tests unitaires, il vous suffit de remplacer le code de la méthode configuration
(
) vu ci-dessus, par celui-ci :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
@Configuration
public
static
Option[] configuration
(
) throws
Exception
{
File customKarafDistribution =
new
File (
"C:/Users/orozier/workspaceEclipse/customerRestFulHibernateWS/module-karaf-custom-distribution/target/module-karaf-custom-distribution-1.0.0.zip"
);
return
new
Option[]
{
karafDistributionConfiguration
(
).frameworkUrl
(
customKarafDistribution.toURI
(
).toString
(
))
.name
(
"Karaf custom distrib"
)
.unpackDirectory
(
new
File
(
"target/paxexam/unpack/"
))
.useDeployFolder
(
false
),
configureConsole
(
).ignoreLocalConsole
(
),
logLevel
(
LogLevel.INFO),
keepRuntimeFolder
(
),
//Installation des bundles JUnit
junitBundles
(
)
}
;
}
Vous n'avez plus à utiliser le fichier « src/main/resources/etc/org.ops4j.datasource-customerDb.cfg » permettant de configurer la source de données.
IV-C. Configuration des méthodes de test▲
Toute méthode utilisant l'annotation « @Test » sera exécutée dans le framework OSGI lancé par le container de test (dans notre cas : Karaf).
Face à une telle méthode, « Pax Exam » construit un bundle à la volée, appelé « test prob », qui contient la classe de test et installe ce bundle dans Karaf.
En utilisant les attributs de la classe de test annotés par « @Inject », le corps des méthodes de test peut accéder au bundle context du « test prob » ou à n'importe quel service obtenu depuis le registre des services OSGI.
L'annotation « @Inject » permet d'injecter des dépendances dans la classe de test.
L'annotation « @Filter » peut être ajoutée pour restreindre l'ensemble des services. Cette annotation possède un argument optionnel nommé « timeout » qui spécifie la durée, en millisecondes, durant laquelle la présence du service est testée.
Dans le paragraphe précédent, nous avons installé :
- une Datasource ;
- les features nécessaires à nos projets.
Dès lors, il est intéressant de créer des méthodes de test, dans la classe abstraite « KarafContainer », qui permettent de tester la bonne installation des features et le bon fonctionnement de la Datasource.
Méthode de test de la classe KarafContainer
À partir de ce que nous venons d'expliquer, nous pouvons donc compléter notre classe abstraite 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.
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.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
package
com.exemple.customerRestFulHibernateWS.test;
import
static
org.ops4j.pax.exam.CoreOptions.junitBundles;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.configureConsole;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
import
static
org.ops4j.pax.exam.karaf.options.KarafDistributionOption.logLevel;
import
java.io.File;
import
javax.inject.Inject;
import
org.apache.karaf.features.BootFinished;
import
org.junit.Test;
import
org.junit.runner.RunWith;
import
org.ops4j.pax.exam.Configuration;
import
org.ops4j.pax.exam.Option;
import
org.ops4j.pax.exam.junit.PaxExam;
import
org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel;
import
org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import
org.ops4j.pax.exam.spi.reactors.PerClass;
import
javax.sql.DataSource;
import
java.sql.Connection;
import
java.sql.ResultSet;
import
java.sql.SQLException;
import
java.sql.Statement;
import
static
org.hamcrest.CoreMatchers.is;
import
static
org.hamcrest.CoreMatchers.notNullValue;
import
static
org.junit.Assert.assertEquals;
import
static
org.junit.Assert.assertThat;
import
static
org.junit.Assert.assertTrue;
import
org.ops4j.pax.exam.util.Filter;
import
org.osgi.framework.BundleContext;
import
org.apache.karaf.features.FeaturesService;
@RunWith
(
PaxExam.class
) //Permet de lier Pax Exam a JUnit afin de mettre en place un container de test (Karaf) contenant un framework OSGI et nos bundles a tester
@ExamReactorStrategy
(
PerClass.class
) //Determine si le framework OSGI doit redemarrer ou pas pour chaque test.
public
abstract
class
KarafContainer
{
@Inject
//Annotation permettant d'injecter des dependances dans la classe de test
@Filter
(
"(osgi.jndi.service.name=customerDb)"
) //L'annotation Filter peut etre ajoutee pour restreindre l'ensemble des services. Cette annotation possede un argument optionnel nomme "timeout" qui specifie la duree, en millisecondes, durant laquelle la presence du service est testee
DataSource dataSource;
//Declaration d'un attribut de type BundleContext qui recupere le context du "test prob"
@Inject
private
BundleContext bc;
//Declaration d'un attribut de type FeaturesService afin de verifier la bonne installation des features
@Inject
protected
FeaturesService featuresService;
//Declaration d'un attribut de type BootFinished pour etre sur que les tests se font uniquement si les features de demarrage sont toutes installees
@Inject
BootFinished bootFinished;
//Declaration des versions definies dans le fichier module-kar/feature.xml
public
static
final
String VERSION_HIBERNATE_ORM =
"5.1.0.Final"
;
public
static
final
String VERSION_CXF =
"3.1.5"
;
public
static
final
String VERSION_PAX_JDBC =
"0.7.0"
;
public
static
final
String VERSION_JPA =
"2.3.0"
;
public
static
final
String VERSION_TRANSACTION =
"1.3.0"
;
public
static
final
String VERSION_JNDI =
"4.0.4"
;
@Configuration
public
static
Option[] configuration
(
) throws
Exception
{
File customKarafDistribution =
new
File (
"C:/Users/orozier/workspaceEclipse/customerRestFulHibernateWS/module-karaf-custom-distribution/target/module-karaf-custom-distribution-1.0.0.zip"
);
return
new
Option[]
{
karafDistributionConfiguration
(
).frameworkUrl
(
customKarafDistribution.toURI
(
).toString
(
))
.name
(
"Karaf custom distrib"
)
.unpackDirectory
(
new
File
(
"target/paxexam/unpack/"
))
.useDeployFolder
(
false
),
configureConsole
(
).ignoreLocalConsole
(
),
logLevel
(
LogLevel.INFO),
keepRuntimeFolder
(
),
//Installation des bundles JUnit
junitBundles
(
)
/*karafDistributionConfiguration().frameworkUrl(maven().groupId("org.apache.karaf").artifactId("apache-karaf")
.type("zip").version("4.0.4"))
.unpackDirectory(new File("target/paxexam/unpack/"))
.useDeployFolder(false),
configureConsole().ignoreLocalConsole(),
logLevel(LogLevel.INFO),
keepRuntimeFolder(),
//Installation des features preinstallees dans Karaf
features(maven().groupId("org.apache.karaf.features").artifactId("enterprise").type("xml").classifier("features").version("4.0.4"),"jdbc"),
features(maven().groupId("org.apache.karaf.features").artifactId("enterprise").type("xml").classifier("features").version("4.0.4"),"jpa"),
features(maven().groupId("org.apache.karaf.features").artifactId("enterprise").type("xml").classifier("features").version("4.0.4"),"transaction"),
features(maven().groupId("org.apache.karaf.features").artifactId("enterprise").type("xml").classifier("features").version("4.0.4"),"jndi"),
//Installation des features supplementaires
features(maven().groupId("org.apache.cxf.karaf").artifactId("apache-cxf").type("xml").classifier("features").version("3.1.5"),"cxf"),
features(maven().groupId("org.apache.karaf.features").artifactId("enterprise").type("xml").classifier("features").version("4.0.4"),"pax-jdbc"),
features(maven().groupId("org.apache.karaf.features").artifactId("enterprise").type("xml").classifier("features").version("4.0.4"),"pax-jdbc-config"),
features(maven().groupId("org.apache.karaf.features").artifactId("enterprise").type("xml").classifier("features").version("4.0.4"),"pax-jdbc-pool-dbcp2"),
features(maven().groupId("org.hibernate").artifactId("hibernate-osgi").type("xml").classifier("karaf").version("5.1.0.Final"),"hibernate-orm"),
editConfigurationFilePut("etc/org.ops4j.pax.web.cfg", "org.osgi.service.http.port", "alternative-port"),
//Configuration du fichier "etc/org.ops4j.pax.url.mvn.cfg" pour renseigner notre repository local (derniere ligne)
editConfigurationFilePut( "etc/org.ops4j.pax.url.mvn.cfg", "org.ops4j.pax.url.mvn.repositories", "http://repo1.maven.org/maven2@id=central, "
+ "http://repository.springsource.com/maven/bundles/release@id=spring.ebr.release, "
+ "http://repository.springsource.com/maven/bundles/external@id=spring.ebr.external, "
+ "http://zodiac.springsource.com/maven/bundles/release@id=gemini, "
+ "http://repository.apache.org/content/groups/snapshots-group@id=apache@snapshots@noreleases, "
+ "https://oss.sonatype.org/content/repositories/snapshots@id=sonatype.snapshots.deploy@snapshots@noreleases, "
+ "https://oss.sonatype.org/content/repositories/ops4j-snapshots@id=ops4j.sonatype.snapshots.deploy@snapshots@noreleases, "
+ "http://repository.springsource.com/maven/bundles/external@id=spring-ebr-repository@snapshots@noreleases, "
+ "file://C:/Users/orozier/.m2/repository@id=1@snapshots"),
//Installation du driver PostgreSql
mavenBundle().groupId("org.postgresql").artifactId("postgresql").version("9.4-1203-jdbc41"),
//Creation de la Datasource
replaceConfigurationFile("etc/org.ops4j.datasource-customerDb.cfg", new File("src/test/resources/org.ops4j.datasource-customerDb.cfg")),
//Installation des bundles JUnit
junitBundles(),
//Installation de nos bundles
mavenBundle().groupId("com.exemple.customerRestFulHibernateWS").artifactId("module-data").version("1.0.0"),
mavenBundle().groupId("com.exemple.customerRestFulHibernateWS").artifactId("module-services").version("1.0.0"),
mavenBundle().groupId("com.exemple.customerRestFulHibernateWS").artifactId("module-jaxrs").version("1.0.0")*/
}
;
}
/**
* Teste l'existence du Bundle context du "test prob"
*/
@Test
public
void
shouldHaveBundleContext
(
)
{
assertThat
(
bc, is
(
notNullValue
(
)));
}
/**
* Teste la bonne installation de la feature jdbc
*
@throws
Exception
*/
@Test
public
void
verifJdbcFeature
(
) throws
Exception
{
//On ne verifie pas la version car quelle que soit cette version, la feature jdbc embarquee dans Karaf permettra toujours de saisir des requetes sur la base de donnees via la console Karaf
assertThat
(
featuresService.isInstalled
(
featuresService.getFeature
(
"jdbc"
)), is
(
true
));
}
/**
* Teste la bonne installation de la feature jpa
*
@throws
Exception
*/
@Test
public
void
verifJpaFeature
(
) throws
Exception
{
assertTrue
(
featuresService.isInstalled
(
featuresService.getFeature
(
"jpa"
, VERSION_JPA)));
}
/**
* Teste la bonne installation de la feature transaction
*
@throws
Exception
*/
@Test
public
void
verifTransactionFeature
(
) throws
Exception
{
assertTrue
(
featuresService.isInstalled
(
featuresService.getFeature
(
"transaction"
, VERSION_TRANSACTION)));
}
/**
* Teste la bonne installation de la feature jndi
*
@throws
Exception
*/
@Test
public
void
verifJndiFeature
(
) throws
Exception
{
assertTrue
(
featuresService.isInstalled
(
featuresService.getFeature
(
"jndi"
, VERSION_JNDI)));
}
/**
* Teste la bonne installation de la feature cxf
*
@throws
Exception
*/
@Test
public
void
verifCxfFeature
(
) throws
Exception
{
assertTrue
(
featuresService.isInstalled
(
featuresService.getFeature
(
"cxf"
, VERSION_CXF)));
}
/**
* Teste la bonne installation de la feature pax-jdbc
*
@throws
Exception
*/
@Test
public
void
verifPaxJdbcFeature
(
) throws
Exception
{
assertTrue
(
featuresService.isInstalled
(
featuresService.getFeature
(
"pax-jdbc"
, VERSION_PAX_JDBC)));
}
/**
* Teste la bonne installation de la feature pax-jdbc-config
*
@throws
Exception
*/
@Test
public
void
verifPaxJdbcConfigFeature
(
) throws
Exception
{
assertTrue
(
featuresService.isInstalled
(
featuresService.getFeature
(
"pax-jdbc-config"
, VERSION_PAX_JDBC)));
}
/**
* Teste la bonne installation de la feature pax-jdbc-pool-dbcp2
*
@throws
Exception
*/
@Test
public
void
verifPaxJdbcPoolDbcp2Feature
(
) throws
Exception
{
assertTrue
(
featuresService.isInstalled
(
featuresService.getFeature
(
"pax-jdbc-pool-dbcp2"
, VERSION_PAX_JDBC)));
}
/**
* Teste la bonne installation de la feature hibernate-orm
*
@throws
Exception
*/
@Test
public
void
verifHibernateOrmFeature
(
) throws
Exception
{
assertTrue
(
featuresService.isInstalled
(
featuresService.getFeature
(
"hibernate-orm"
, VERSION_HIBERNATE_ORM)));
}
/**
* Teste l'accessibilite de la base de donnees
*
@throws
SQLException
*/
@Test
public
void
testDataSourceFromConfig
(
) throws
SQLException
{
Connection connection =
dataSource.getConnection
(
);
Statement statement =
connection.createStatement
(
);
statement.execute
(
"CREATE TABLE ctm.t1 (col1 INTEGER NOT NULL, col2 CHAR(25), PRIMARY KEY (COL1)) "
);
statement.executeUpdate
(
"insert into ctm.t1 (col1, col2) values(101, 'test JUnit')"
);
ResultSet result =
statement.executeQuery
(
"select col1 from ctm.t1 where col2 = 'test JUnit'"
);
while
(
result.next
(
))
{
assertEquals
(
101
, result.getInt
(
"col1"
));
}
result.close
(
);
statement.execute
(
"DROP TABLE ctm.T1"
);
statement.close
(
);
connection.close
(
);
}
}
Comme vous pouvez le voir, nous avons ajouté les attributs de type :
- « DataSource » qui récupère, via l'annotation « @Filter », la Datasource installée sous forme de service dans Karaf. Cet attribut est ensuite utilisé par la méthode de test « testDataSourceFromConfig() » ;
- « BundleContext » qui récupère le contexte du « test prob » afin de vérifier que « Pax Exam » a bien créé un bundle à la volée concernant notre classe de test ;
- « FeaturesService » qui permet de vérifier la bonne installation des features dans Karaf ;
- « BootFinished » qui permet d'être sûr que les tests seront réalisés seulement si les features de démarrage ont été installées.
La méthode « testDataSourceFromConfig() » constitue un test unitaire dont le but est de vérifier l'accessibilité de la base de données. Pour ce faire, cette méthode crée une table dans la base de données, y insère un enregistrement, le sélectionne puis supprime la table.
Les méthodes « checkXxxxxxxxFeature() » permettent de tester la bonne installation de chacune des features installées dans Karaf.
Méthode de test de la classe CustomerDaoTest
Il est désormais indispensable que notre classe CustomerDaoTest hérite de notre classe abstraite KarafContaineravant d'y ajouter des méthodes de test faisant référence à la classe CustomerDaoImpl. La classe CustomerDaoTest ressemblera donc à ceci :
2.
3.
4.
5.
6.
7.
package
com.exemple.customerRestFulHibernateWS.test;
public
class
CustomerDaoTest extends
KarafContainer
{
}
Cette classe contiendra des références à l'interface CustomerDao qui appartient au module « module-data ». Il est donc nécessaire d'adapter le fichier « module-test/pom.xml » en y ajoutant :
-
la dépendance à l'artefact « module-data » :
module-test/pom.xmlSélectionnez1.
2.
3.
4.
5.
6.<dependency>
<groupId>
com.exemple.customerRestFulHibernateWS</groupId>
<artifactId>
module-data</artifactId>
<version>
[1.0.0,2.0)</version>
<type>
bundle</type>
</dependency>
- Le plugin « maven-bundle-plugin » afin d'importer le package « com.exemple.customerRestFulHibernateWS.data.CustomerDao » :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<plugin>
<groupId>
org.apache.felix</groupId>
<artifactId>
maven-bundle-plugin</artifactId>
<version>
3.0.0</version>
<extensions>
true</extensions>
<configuration>
<instructions>
<!-- Le fichier MANIFEST.MF sera rempli avec les informations ci-dessous. Pour plus d'informations : http://wiki.osgi.org/wiki/Category:Manifest_Header -->
<Bundle-SymbolicName>
${project.artifactId}</Bundle-SymbolicName>
<!-- Nom qui identifie le bundle de manière unique -->
<Bundle-Version>
${project.version}</Bundle-Version>
<!-- Decrit la version du bundle et permet l'activation simultanee de plusieurs versions d'un bundle dans la meme instance de structure -->
<Import-Package>
*</Import-Package>
</instructions>
</configuration>
</plugin>
La classe CustomerDaoTest aura pour rôle de tester les méthodes de la classe abstraite CustomerDao contenue dans le module « module-data ». Pour cette raison, dans la classe CustomerDaoTest, nous allons déclarer un attribut de type CustomerDao que nous allons annoter à l'aide de l'annotation « @Inject ». Cette déclaration aura pour effet d'injecter un service « CustomerDao » dans notre container dans le but de tester ses méthodes.
Dans ce tutoriel, concernant la classe CustomerDaonous nous limiterons à réaliser les tests unitaires sur la méthode findCustomer
(
final
long
customerId). Il va de soi que, dans vos projets, il sera nécessaire de réaliser les tests unitaires sur toutes les méthodes de vos classes.
Ainsi, notre classe CustomerDaoTest ressemblera à ceci :
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.
package
com.exemple.customerRestFulHibernateWS.test;
import
static
org.junit.Assert.assertEquals;
import
static
org.junit.Assert.assertNull;
import
java.sql.Connection;
import
java.sql.ResultSet;
import
java.sql.SQLException;
import
java.sql.Statement;
import
javax.inject.Inject;
import
org.junit.Test;
import
com.exemple.customerRestFulHibernateWS.data.CustomerDao;
public
class
CustomerDaoTest extends
KarafContainer
{
@Inject
private
CustomerDao customerDao;
/**
* Teste la methode findCustomer(final long customerId) de la classe CustomerDaoImpl
*
@throws
SQLException
*/
@Test
public
void
testFindCustomer
(
) throws
SQLException
{
Connection connection =
dataSource.getConnection
(
);
Statement statement =
connection.createStatement
(
);
ResultSet result =
null
;
try
{
statement.executeUpdate
(
"insert into ctm.customer (prenom, nom) values('Eddy', 'Sion')"
);
result =
statement.executeQuery
(
"select idcustomer, prenom, nom from ctm.customer where nom = 'Sion'"
);
while
(
result.next
(
))
{
//Verification que l'enregistrement retourne est celui escompte
assertEquals
(
"Customer[idCustomer="
+
result.getLong
(
"idcustomer"
) +
", nom=Sion, prenom=Eddy]"
, customerDao.findCustomer
(
result.getLong
(
"idcustomer"
)).toString
(
));
//Verification que la methode retourne null en cas d'enregistrement non trouve
assertNull
(
customerDao.findCustomer
(
0
));
}
}
catch
(
Exception e)
{
e.printStackTrace
(
);
}
finally
{
result.close
(
);
statement.executeUpdate
(
"delete from ctm.customer where nom = 'Sion'"
);
statement.close
(
);
connection.close
(
);
}
}
}
Méthode de test de la classe CustomerServiceTest
-
Complétez le fichier « module-test/pom.xml » pour ajouter la dépendance concernant le module « module-services » :
module-test/pom.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.<dependencies>
<dependency>
<groupId>
com.exemple.customerRestFulHibernateWS</groupId>
<artifactId>
module-data</artifactId>
<version>
[1.0.0,2.0)</version>
<type>
bundle</type>
</dependency>
<dependency>
<groupId>
com.exemple.customerRestFulHibernateWS</groupId>
<artifactId>
module-services</artifactId>
<version>
[1.0.0,2.0)</version>
<type>
bundle</type>
</dependency>
- La classe CustomerServiceTest peut désormais être créée dans le module « module-test » et plus précisément dans le package « com.exemple.customerRestFulHibernateWS.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.
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.
package
com.exemple.customerRestFulHibernateWS.test;
import
static
org.junit.Assert.assertEquals;
import
static
org.junit.Assert.assertNull;
import
java.sql.Connection;
import
java.sql.ResultSet;
import
java.sql.SQLException;
import
java.sql.Statement;
import
javax.inject.Inject;
import
org.junit.Test;
import
com.exemple.customerRestFulHibernateWS.services.CustomerService;
public
class
CustomerServiceTest extends
KarafContainer
{
@Inject
private
CustomerService customerService;
/**
* Teste la methode getCustomerById(long customerId) de la classe CustomerService
*
@throws
SQLException
*/
@Test
public
void
testGetCustomerById
(
) throws
SQLException
{
Connection connection =
dataSource.getConnection
(
);
Statement statement =
connection.createStatement
(
);
ResultSet result =
null
;
try
{
statement.executeUpdate
(
"insert into ctm.customer (prenom, nom) values('Eddy', 'Sion')"
);
result =
statement.executeQuery
(
"select idcustomer, prenom, nom from ctm.customer where nom = 'Sion'"
);
while
(
result.next
(
))
{
//Verification que l'enregistrement retourne est celui escompte
assertEquals
(
"Customer[idCustomer="
+
result.getLong
(
"idcustomer"
) +
", nom=Sion, prenom=Eddy]"
, customerService.getCustomerById
(
result.getLong
(
"idcustomer"
)).toString
(
));
//Verification que la methode retourne null en cas d'enregistrement non trouve
assertNull
(
customerService.getCustomerById
(
0
));
}
}
catch
(
Exception e)
{
e.printStackTrace
(
);
}
finally
{
result.close
(
);
statement.executeUpdate
(
"delete from ctm.customer where nom = 'Sion'"
);
statement.close
(
);
connection.close
(
);
}
}
}
Dans ce tutoriel, concernant la classe CustomerServiceTest nous nous limiterons à réaliser les tests unitaires sur la méthode getCustomerById
(
long
customerId). Il va de soi que, dans vos projets, il sera nécessaire de réaliser les tests unitaires sur toutes les méthodes de vos classes.
Méthode de test de la classe CustomerRestServiceTest
Peut-être l'avez-vous remarqué lors de la présentation des classes CustomerDaoTest et CustomerServiceTest, et plus précisément de leur méthode de test testFindCustomer et getCustomerById, on déclare, dans ces méthodes, un attribut du type de l'interface implémentée par la classe à tester. On ne peut pas déclarer une instance de la classe qu'on teste sous peine de voir toutes les méthodes de test en erreur.
Or, dans notre tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf », la classe CustomerRestService que nous cherchons à tester n'implémente aucune interface. Il est donc nécessaire de créer une interface qui sera implémentée par la classe CustomerRestService.
Pour cela,
-
dans le module « module-jaxrs », créez un nouveau package « com.exemple.customerRestFulHibernateWSJaxrs.impl » en cliquant droit sur le répertoire « src/main/java », puis en choisissant « New », puis « Package » tel que présenté sur la capture d'écran ci-dessous :
-
Saisissez le nom du package puis cliquez sur le bouton « Finish » :
-
Par un clic droit sur le fichier « CustomerRestService.java », choisissez « Copy » :
-
Par un clic droit sur le nouveau package, choisissez « Paste » :
-
Dans le package « com.exemple.customerRestFulHibernateWS.jaxrs », supprimez le fichier « CustomerRestService.java » en cliquant droit sur le fichier et en choisissant « Delete » :
Confirmez par « Ok » :
-
Renommez le fichier « CustomerRestService.java » en « CustomerRestServiceImpl.java » en cliquant droit dessus puis en choisissant « Refactor », puis « Rename » :
Entrez alors le nouveau nom :
-
Désormais, vous pouvez créer l'interface CustomerRestService en cliquant droit sur le package « com.exemple.customerRestFulHibernateWS.jaxrs » puis en choisissant « New », puis « Interface » :
Remplissez la fenêtre qui apparaît avec le nom de l'interface :
-
Complétez cette nouvelle interface avec les informations ci-dessous :
module-jaxrs/CustomerRestService.javaSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.package
com.exemple.customerRestFulHibernateWS.jaxrs;import
java.util.List;import
javax.ws.rs.core.Response;import
com.exemple.customerRestFulHibernateWS.data.model.Customer;import
com.exemple.customerRestFulHibernateWS.data.model.Order;import
com.exemple.customerRestFulHibernateWS.data.model.Product;public
interface
CustomerRestService{
public
CustomergetCustomer
(
long
customerId);public
ResponseupdateCustomer
(
Customer customer);public
ResponseaddOrderToCustomer
(
long
customerId, Order order);public
ResponseaddCustomer
(
Customer customer);public
ResponsedeleteCustomer
(
long
customerId);public
List<
Order>
getOrder
(
long
customerId);public
OrdergetOrder
(
long
customerId,long
orderId);public
ProductgetProduct
(
long
customerId,long
orderId,long
productId);public
List<
Product>
getProducts
(
long
customerId,long
orderId);}
-
Modifiez le fichier « my-service.xml » du module « module-jaxrs » pour y insérer la ligne :
<service
ref
=
"customerRestService"
interface
=
"com.exemple.customerRestFulHibernateWS.jaxrs.CustomerRestService"
/>
Le fichier « my-service.xml » ressemble donc à ceci :
module-jaxrs/my-service.xmlSélectionnez1.
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.<blueprint
xmlns
=
"http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns
:
xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns
:
jaxws
=
"http://cxf.apache.org/blueprint/jaxws"
xmlns
:
jaxrs
=
"http://cxf.apache.org/blueprint/jaxrs"
xmlns
:
cxf
=
"http://cxf.apache.org/blueprint/core"
xsi
:
schemaLocation
=
"
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://cxf.apache.org/blueprint/jaxws http://cxf.apache.org/schemas/blueprint/jaxws.xsd
http://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd
http://cxf.apache.org/blueprint/core http://cxf.apache.org/schemas/blueprint/core.xsd"
>
<!-- Configuration des logs CXF afin de faire apparaître les requetes recues et les reponses fournies, dans le journal de Karaf -->
<
cxf
:
bus
id
=
"bus"
>
<
cxf
:
features>
<
cxf
:
logging/>
</
cxf
:
features>
</
cxf
:
bus>
<!-- Configuration du endpoint JAX-RS dans le container OSGI-->
<
jaxrs
:
server
address
=
"/olivier"
id
=
"olivier"
>
<
jaxrs
:
serviceBeans>
<ref
component-id
=
"customerRestService"
/>
</
jaxrs
:
serviceBeans>
</
jaxrs
:
server>
<service
ref
=
"customerRestService"
interface
=
"com.exemple.customerRestFulHibernateWS.jaxrs.CustomerRestService"
/>
<!-- Declaration du bean customerRestService implemente par le POJO com.exemple.karaf.jaxrs.CustomerRestService-->
<!-- Ce bean possede une propriete a injecter. L'injection de cette propriete (ici une instance de la classe CustomerService) est faite
immediatement apres la creation du bean -->
<bean
id
=
"customerRestService"
class
=
"com.exemple.customerRestFulHibernateWS.jaxrs.CustomerRestServiceImpl"
>
<property
name
=
"customerService"
ref
=
"customerService"
/>
</bean>
<!-- Definition des dependances -->
<reference
id
=
"customerService"
interface
=
"com.exemple.customerRestFulHibernateWS.services.CustomerService"
/>
</blueprint>
-
Complétez la classe CustomerRestServiceImpl afin qu'elle implémente l'interface CustomerRestService comme le présente la capture d'écran ci-dessous :
-
Complétez le fichier « module-test.pom.xml » pour ajouter la dépendance concernant le module « module-jaxrs » :
module-test/pom.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.<dependencies>
<dependency>
<groupId>
com.exemple.customerRestFulHibernateWS</groupId>
<artifactId>
module-data</artifactId>
<version>
[1.0.0,2.0)</version>
<type>
bundle</type>
</dependency>
<dependency>
<groupId>
com.exemple.customerRestFulHibernateWS</groupId>
<artifactId>
module-services</artifactId>
<version>
[1.0.0,2.0)</version>
<type>
bundle</type>
</dependency>
<dependency>
<groupId>
com.exemple.customerRestFulHibernateWS</groupId>
<artifactId>
module-jaxrs</artifactId>
<version>
[1.0.0,2.0)</version>
<type>
bundle</type>
</dependency>
- Désormais, nous pouvons créer, dans le module « module-test », la classe CustomerRestServiceTest qui nous permettra de tester les méthodes déclarées dans l'interface CustomerRestService. Nous créerons cette classe dans le package « com.exemple.customerRestFulHibernateWS.test » de la même manière que nous avons créé les classes de test précédentes :
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.
package
com.exemple.customerRestFulHibernateWS.test;
import
static
org.junit.Assert.assertEquals;
import
static
org.junit.Assert.assertNull;
import
java.sql.Connection;
import
java.sql.ResultSet;
import
java.sql.SQLException;
import
java.sql.Statement;
import
javax.inject.Inject;
import
org.junit.Test;
import
com.exemple.customerRestFulHibernateWS.jaxrs.CustomerRestService;
public
class
CustomerRestServiceTest extends
KarafContainer
{
@Inject
private
CustomerRestService customerRestService;
/**
* Teste la methode getCustomer(final long customerId) de la classe CustomerRestService
*
@throws
SQLException
*/
@Test
public
void
testGetCustomer
(
) throws
SQLException
{
Connection connection =
dataSource.getConnection
(
);
Statement statement =
connection.createStatement
(
);
ResultSet result =
null
;
try
{
statement.executeUpdate
(
"insert into ctm.customer (prenom, nom) values('Eddy', 'Sion')"
);
result =
statement.executeQuery
(
"select idcustomer, prenom, nom from ctm.customer where nom = 'Sion'"
);
while
(
result.next
(
))
{
//Verification que l'enregistrement retourne est celui escompte
assertEquals
(
"Customer[idCustomer="
+
result.getLong
(
"idcustomer"
) +
", nom=Sion, prenom=Eddy]"
, customerRestService.getCustomer
(
result.getLong
(
"idcustomer"
)).toString
(
));
//Verification que la methode retourne null en cas d'enregistrement non trouve
assertNull
(
customerRestService.getCustomer
(
0
));
}
}
catch
(
Exception e)
{
e.printStackTrace
(
);
}
finally
{
result.close
(
);
statement.executeUpdate
(
"delete from ctm.customer where nom = 'Sion'"
);
statement.close
(
);
connection.close
(
);
}
}
}
Dans ce tutoriel, concernant la classe CustomerRestService nous nous limiterons à réaliser les tests unitaires sur la méthode getCustomer
(
long
customerId). Il va de soi que, dans vos projets, il sera nécessaire de réaliser les tests unitaires sur toutes les méthodes de vos classes.
V. Lancement des tests▲
V-A. Cas où certains modules du projet ont été modifiés▲
Pour lancer les tests unitaires, il vous est nécessaire, du fait que nous avons modifié le module « module-jaxrs », de recompiler l'ensemble du projet par un clic droit sur le projet puis de choisir « Run As », puis « Maven Build ».
Dans la fenêtre qui apparaît, saisissez « clean install » puis appuyez sur le bouton « run » comme le présente la capture d'écran ci-dessous :
À l'issue de cette compilation, les modifications effectuées dans les modules constituant le projet sont prises en compte et une nouvelle distribution de Karaf personnalisée est générée.
Cette distribution est utilisée par « Pax Exam » pour exécuter les tests unitaires.
Un onglet « JUnit » apparaît dans l'interface d'Eclipse pour faire figurer le résultat des tests :
Si cet onglet n'apparaît pas, choisissez « Show view » dans le menu « Window » d'Eclipse puis « Other » puis, dans le filtre qui apparaît, saisissez « junit ». Sélectionnez alors « JUnit » et appuyez sur le bouton « OK ».
V-B. Cas où seul le module « module-test » a été modifié▲
Dans le cas où vous avez modifié uniquement le contenu de vos tests unitaires, vous pouvez lancer les tests unitaires en cliquant droit sur l'une de vos classes de test puis en choisissant « Run As », puis « JUnit Test » comme présenté ci-dessous :
VI. Code source de l'exemple▲
Le code source de l'exemple utilisé dans ce tutoriel est disponible à cette adresse :
Il s'agit du code source utilisé dans le tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf » auquel nous avons ajouté les tests unitaires décrits plus haut.
VII. Conclusion▲
Ce tutoriel, complément du tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf » conclut notre apprentissage, sous Eclipse, du développement de services web REST destinés à être déployés sous le container Karaf.
Au fil de ces deux tutoriels, nous avons donc pu voir la manière de découper un service web REST en couches applicatives. Chacune de ces couches peut facilement être développée dans un sous-module Maven. À la fin du développement de ces sous-modules, plusieurs choix de déploiement s'offrent à nous :
- par archive Kar ;
- par feature ;
- par génération d'une distribution personnalisée de Karaf.
C'est ce dernier choix qui se révèle le plus judicieux si on souhaite épargner à l'équipe d'exploitation en charge de l'installation de notre projet :
- la configuration des DataSource Factory ;
- la configuration des sources de données ;
- l'installation d'Hibernate ;
- l'installation de pax-jdbc ;
- l'installation des pilotes de la base de données ;
- l'installation de cxf ;
- le déploiement des bundles ou de l'archive « kar ».
Entendez également par tous ces avantages que l'équipe d'exploitation n'aura pas à se soucier des versions des différentes features. Ce qui est un avantage non négligeable pour l'équipe de développement également puisqu'elle contrôle la version des features intervenant dans le projet de bout en bout.
L'autre avantage non négligeable de l'utilisation d'une distribution personnalisée de Karaf concerne les tests unitaires que nous avons découverts dans le présent tutoriel.
En effet, à l'issue du développement, non seulement les tests unitaires sont effectués, mais ils le sont sur une distribution de Karaf dans laquelle tous les développements sont déjà intégrés.
Il en résulte un gain de temps énorme lors de la phase d'intégration des bundles dans Karaf (avec tout ce que cela implique : installation, tests…) puisqu'elle est réalisée durant la phase de développement.
Nous avons pu constater que les tests unitaires de bundles OSGI sont effectués facilement en utilisant le framework « Pax Exam ». Pour les réaliser, Pax Exam installe un container OSGI et y installe les bundles que nous lui avons indiqués par l'injection des services que nous lui spécifions dans chacune des classes de test.
Je tiens à remercier Mickaël Baron pour la relecture technique de cet article ainsi que f-leb pour la relecture orthographique. Ces courageux font un travail pas toujours très motivant, mais toujours efficacement. Merci à vous !