Tutoriel pour mettre en place et exécuter les tests unitaires sur un projet multibundle déployé sous Karaf et développé sous Eclipse

Tests unitaires sur un projet multibundle sous Eclipse et déployé sous Karaf

Dans notre tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf », nous avons étudié la manière de créer un projet multibundle destiné à être déployé sous Karaf et développé sous Eclipse.

Nous avons également vu les différentes méthodes de déploiement d'une telle application en poussant la problématique jusqu'à créer une distribution personnalisée de Karaf.

Aujourd'hui, par le présent tutoriel, nous allons parler de la manière d'exécuter les tests unitaires sur un tel projet en reprenant le code source du tutoriel ci-dessus évoqué.

Avant de commencer à lire ce tutoriel, nous vous conseillons donc de lire le tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf » afin de comprendre, en détail, la manière dont est articulé un tel projet.

Pour réaliser les tests unitaires, nous utiliserons le framework « Pax Exam » tout en réutilisant la distribution personnalisée de Karaf que nous avons générée auparavant.

Après avoir lu notre précédent tutoriel puis celui-ci, vous constaterez la facilité, sous Eclipse :

  • à créer un service web REST composé de plusieurs modules ;
  • à les intégrer sous Karaf ;
  • à réaliser les tests unitaires sur un tel projet.

Les deux derniers points pouvant être réalisés à l'issue d'une seule et même compilation.

Pour réagir au contenu de ce tutoriel, un espace de dialogue vous est proposé sur le forum : Commentez Donner une note à l'article (5)

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

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

  1. Cliquez droit sur le projet « customerRestFulHibernateWS » → « New » → « Project ». Dans la fenêtre qui apparaît, saisissez « maven » dans le filtre puis choisissez « Maven module ».

    Image non disponible

    Cliquez sur le bouton « Next ».

  2. 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 ».

    Image non disponible

    Cliquez sur le bouton « Next ».

  3. 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 ».

    Image non disponible
  4. 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.test

    Image non disponible

    Cliquez sur le bouton « Finish ».

  5. Dans l'explorateur de projets d'Eclipse, nous pouvons voir que notre projet « customerRestFulHibernateWS » a été complété par le module « module-test » :

    Image non disponible

    Le fichier « pom.xml » a été modifié pour y inclure le nouveau module (« module-test ») :

    Image non disponible

    On peut également voir qu'un nouveau projet nommé « module-test » a été créé :

    Image non disponible
  6. 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… ».

  7. 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 :
Image non disponible

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 :

module-test/MyTest.java
Sélectionnez
1.
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 :

  1. 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 » :

    Image non disponible
  2. Dans la section « Name », saisissez « KarafContainer » et cochez la case « abstract » :

    Image non disponible

    Cliquez sur « Finish ». Le fichier « KarafContainer.java » s'ouvre alors.

  3. 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 :
module-test/KarafContainer
Sélectionnez
1.
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 :

Options de configuration
Sélectionnez
1.
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 :

 
Sélectionnez
1.
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 :

module-test/KarafContainer.java
Sélectionnez
1.
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 :

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 :

module-test/KarafContainer.java
Sélectionnez
1.
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 :

module-test/KarafContainer.java
Sélectionnez
1.
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 :

module-test/CustomerDaoTest.java
Sélectionnez
1.
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.xml
    Sélectionnez
    1.
    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 » :
module-test/pom.xml
Sélectionnez
1.
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 :

module-test/CustomerDaoTest
Sélectionnez
1.
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

  1. Complétez le fichier « module-test/pom.xml » pour ajouter la dépendance concernant le module « module-services » :

    module-test/pom.xml
    Sélectionnez
    1.
    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>
    
  2. 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 :
module-test/CustomerServiceTest.java
Sélectionnez
1.
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,

  1. 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 :

    Image non disponible
  2. Saisissez le nom du package puis cliquez sur le bouton « Finish » :

    Image non disponible
  3. Par un clic droit sur le fichier « CustomerRestService.java », choisissez « Copy » :

    Image non disponible
  4. Par un clic droit sur le nouveau package, choisissez « Paste » :

    Image non disponible
  5. Dans le package « com.exemple.customerRestFulHibernateWS.jaxrs », supprimez le fichier « CustomerRestService.java » en cliquant droit sur le fichier et en choisissant « Delete » :

    Image non disponible

    Confirmez par « Ok » :

    Image non disponible
  6. Renommez le fichier « CustomerRestService.java » en «  CustomerRestServiceImpl.java » en cliquant droit dessus puis en choisissant « Refactor », puis « Rename » :

    Image non disponible

    Entrez alors le nouveau nom :

    Image non disponible
  7. 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 » :

    Image non disponible

    Remplissez la fenêtre qui apparaît avec le nom de l'interface :

    Image non disponible
  8. Complétez cette nouvelle interface avec les informations ci-dessous :

    module-jaxrs/CustomerRestService.java
    Sélectionnez
    1.
    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 Customer getCustomer(long customerId);
         public Response updateCustomer(Customer customer);
         public Response addOrderToCustomer(long customerId, Order order);
         public Response addCustomer(Customer customer);
         public Response deleteCustomer(long customerId);
         public List<Order> getOrder(long customerId);
         public Order getOrder(long customerId,long orderId);
         public Product getProduct(long customerId, long orderId, long productId);
         public List<Product> getProducts(long customerId, long orderId);
    }
    
  9. 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.xml
    Sélectionnez
    1.
    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>
    
  10. Complétez la classe CustomerRestServiceImpl afin qu'elle implémente l'interface CustomerRestService comme le présente la capture d'écran ci-dessous :

    Image non disponible
  11. Complétez le fichier « module-test.pom.xml » pour ajouter la dépendance concernant le module « module-jaxrs » :

    module-test/pom.xml
    Sélectionnez
    1.
    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>
    
  12. 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 :
module-test/CustomerRestServiceTest
Sélectionnez
1.
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 ».

Image non disponible

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 :

Image non disponible

À 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 :

Image non disponible

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 ».

Image non disponible

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 :

Image non disponible

VI. Code source de l'exemple

Le code source de l'exemple utilisé dans ce tutoriel est disponible à cette adresse :

http://olivier-rozier.developpez.com/tutoriels/rest/tests-unitaires-karaf-multibundle/fichiers/customerRestFulHibernateWS.zip

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 !

Index

- A - - J - JPA (1)
API (1) JAXB (1)

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2016 Olivier ROZIER. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.