web-dev-qa-db-fra.com

Implémenter correctement les modules Java dans une construction Maven avec des dépendances de test inter-modules

J'ai un projet multi-modules utilisant Maven et Java. J'essaie maintenant de migrer vers Java 9/10/11 et d'implémenter des modules (comme dans JSR 376: Java Platform Module System =, JPMS) Comme le projet était déjà composé de modules Maven et que les dépendances étaient simples, la création de descripteurs de modules pour le projet était assez simple.

Chaque module Maven possède désormais son propre descripteur de module (module-info.Java), dans le src/main/Java dossier. Il n'y a pas de descripteur de module pour les classes de test.

Cependant, je suis tombé sur un problème que je n'ai pas pu résoudre et je n'ai trouvé aucune description sur la façon de résoudre:

Comment puis-je avoir des dépendances inter-modules test avec Maven et Java?

Dans mon cas, j'ai un module Maven "commun", qui contient des interfaces et/ou des classes abstraites (mais pas d'implémentation concrète). Dans le même module Maven, j'ai des tests abstract pour assurer un bon comportement pour l'implémentation de ces interfaces/classes abstraites. Ensuite, il y a un ou plusieurs sous-modules, avec des implémentations de la classe interface/abstract et des tests étendant le test abstrait.

Cependant, lorsque vous essayez d'exécuter la phase test de la construction Maven, le sous-module échouera avec:

[ERROR] Failed to execute goal org.Apache.maven.plugins:maven-compiler-plugin:3.8.0:testCompile (default-testCompile) on project my-impl-module: Compilation failure: Compilation failure:
[ERROR] C:\projects\com.example\my-module-test\my-impl-module\src\test\Java\com\example\impl\FooImplTest.Java:[4,25] error: cannot find symbol
[ERROR]   symbol:   class FooAbstractTest
[ERROR]   location: package com.example.common

Je soupçonne que cela se produit car les tests ne font pas partie du module. Et même si Maven fait de la "magie" pour faire exécuter les tests dans le cadre du module, cela ne fonctionne pas pour les tests du module dont je dépend (pour une raison quelconque). Comment puis-je réparer ça?

La structure du projet ressemble à ceci ( fichiers de projet de démonstration complets disponibles ici ):

├───my-common-module
│   ├───pom.xml
│   └───src
│       ├───main
│       │   └───Java
│       │       ├───com
│       │       │   └───example
│       │       │       └───common
│       │       │           ├───AbstractFoo.Java (abstract, implements Foo)
│       │       │           └───Foo.Java (interface)
│       │       └───module-info.Java (my.common.module: exports com.example.common)
│       └───test
│           └───Java
│               └───com
│                   └───example
│                       └───common
│                           └───FooAbstractTest.Java (abstract class, tests Foo)
├───my-impl-module
│   ├───pom.xml
│   └───src
│       ├───main
│       │   └───Java
│       │       ├───com
│       │       │   └───example
│       │       │       └───impl
│       │       │           └───FooImpl.Java (extends AbstractFoo)
│       │       └───module-info.Java (my.impl.module: requires my.common.module)
│       └───test
│           └───Java
│               └───com
│                   └───example
│                       └───impl
│                           └───FooImplTest.Java (extends FooAbstractTest)
└───pom.xml

Dépendances dans le my-impl-module/pom.xml est comme suit:

<dependencies>
    <dependency>
        <groupId>com.example</groupId>
        <artifactId>my-common-module</artifactId>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>com.example</groupId>
        <artifactId>my-common-module</artifactId>
        <classifier>tests</classifier> <!-- tried type:test-jar instead, same error -->
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Remarque: ce qui précède n'est qu'un projet que j'ai créé pour illustrer le problème. Le vrai projet est beaucoup plus complexe, et trouvé ici (la branche master est pas encore modulaire), mais le principe est le même.

PS: Je ne pense pas qu'il y ait quelque chose de mal avec le code lui-même, car tout se compile et s'exécute en utilisant le chemin de classe normal (c'est-à-dire dans IntelliJ ou Maven sans les Java). Le problème est introduit avec les modules Java et le chemin du module.

20
haraldK

Sur la base de votre projet de démonstration, j'ai pu reproduire votre erreur. Cela dit, voici les modifications révisées que j'ai apportées, après ma première tentative infructueuse, pour pouvoir construire le projet:

  1. J'ai ajouté le maven-compiler-plugin version 3.8.0 à tous les modules. Vous avez besoin d'une version de 3.7 ou supérieure pour compiler des modules avec Maven - du moins c'est l'avertissement que NetBeans a montré. Comme il n'y a pas de mal, j'ai ajouté le plug-in aux fichiers POM des modules common et implementation:

    <plugin>
        <groupId>org.Apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.0</version>
        <executions>
            <execution>
                <goals>
                    <goal>compile</goal>
                </goals>
                <id>compile</id>
            </execution>
        </executions>
    </plugin> 
    
  2. J'ai exporté les classes de test dans leur propre fichier jar afin qu'elles soient disponibles pour votre module d'implémentation ou n'importe qui d'ailleurs. Pour ce faire, vous devez ajouter ce qui suit à votre my-common-module/pom.xml fichier:

    <plugin>
        <groupId>org.Apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.1.0</version>
        <executions>
            <execution>
                <id>test-jar</id>
                <phase>package</phase>
                <goals>
                    <goal>test-jar</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
    

    Cela exportera my-common-module tester les classes dans -tests.jar fichier - c'est-à-dire .my-common-module-1.0-SNAPSHOT-tests.jar. Notez qu'il n'est pas nécessaire d'ajouter une exécution pour le fichier jar normal comme indiqué dans ce post . Cela introduira cependant une erreur que je traiterai ensuite.

  3. Renommez votre package de test dans my-common-module à com.example.common.test pour que les classes de test soient chargées lors de la compilation des classes de test d'implémentation. Cela corrige le problème de chargement de classe introduit lorsque nous avons exporté les classes de test avec le même nom de package que dans le module où le premier jar, dans ce cas le module, est chargé et le second jar, le fichier jar de test est ignoré. Assez intéressant, je conclus, sur la base d'une observation, que le chemin du module a une priorité plus élevée que le chemin de la classe puisque les paramètres de compilation Maven montrent le tests.jar est spécifié en premier dans le chemin de classe. Fonctionnement mvn clean validate test -X, on voit les paramètres de compilation:

    -d /home/testenv/NetBeansProjects/MavenProject/Implementation/target/test-classes -classpath /home/testenv/NetBeansProjects/MavenProject/Implementation/target/test-classes:/home/testenv/.m2/repository/com/example/Declaration/1.0-SNAPSHOT/Declaration-1.0-SNAPSHOT-tests.jar:/home/testenv/.m2/repository/junit/junit/4.12/junit-4.12.jar:/home/testenv/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar: --module-path /home/testenv/NetBeansProjects/MavenProject/Implementation/target/classes:/home/testenv/.m2/repository/com/example/Declaration/1.0-SNAPSHOT/Declaration-1.0-SNAPSHOT.jar: -sourcepath /home/testenv/NetBeansProjects/MavenProject/Implementation/src/test/Java:/home/testenv/NetBeansProjects/MavenProject/Implementation/target/generated-test-sources/test-annotations: -s /home/testenv/NetBeansProjects/MavenProject/Implementation/target/generated-test-sources/test-annotations -g -nowarn -target 11 -source 11 -encoding UTF-8 --patch-module example.implementation=/home/testenv/NetBeansProjects/MavenProject/Implementation/target/classes:/home/testenv/NetBeansProjects/MavenProject/Implementation/src/test/Java:/home/testenv/NetBeansProjects/MavenProject/Implementation/target/generated-test-sources/test-annotations: --add-reads example.implementation=ALL-UNNAMED
    
  4. Nous devons mettre les classes de test exportées à la disposition du module d'implémentation. Ajoutez cette dépendance à votre my-impl-module/pom.xml:

    <dependency>
        <groupId>com.example</groupId>
        <artifactId>Declaration</artifactId>
        <version>1.0-SNAPSHOT</version>
        <type>test-jar</type>
        <scope>test</scope>
    </dependency>
    
  5. Enfin dans le my-impl-module classe de test, mettez à jour l'importation pour spécifier le nouveau package de test, com.example.common.text, pour accéder à my-common-module classes de test:

    import com.example.declaration.test.AbstractFooTest;
    import com.example.declaration.Foo;
    import org.junit.Test;
    import static org.junit.Assert.*;
    
    /**
     * Test class inheriting from common module...
     */
    public class FooImplementationTest extends AbstractFooTest { ... }
    

Voici les résultats des tests de mon mvn clean package des nouveaux changements:

enter image description here

J'ai mis à jour mon exemple de code dans mon Java-cross-module-testing GitHub repo. La seule question persistante que j'ai, et je suis sûr que vous le faites aussi, est pourquoi cela a-t-il fonctionné lorsque j'ai défini le module d'implémentation comme un projet jar normal au lieu d'un module. Mais ça, je vais jouer avec un autre jour. J'espère que ce que j'ai fourni résout votre problème.

6
Jose Henriquez

J'ai essayé de faire exactement la même chose, il n'est pas possible d'avoir à la fois des tests de boîte blanche et des dépendances de test de module avec la structure de votre projet , mais je pense avoir trouvé un structure alternative qui fait 90% de ce que vous voulez faire:

1/Le problème avec les tests de whitebox est qu'ils fonctionnent avec les patchs de modules, car JPMS n'a pas la notion de test VS main contrairement à Maven. Cela provoque donc des problèmes comme ne pas travailler avec les dépendances de test, ou devoir polluer vos informations de module avec des dépendances de test.

2/Alors, pourquoi ne pas continuer à faire des tests de boîte blanche, mais avec la structure maven des tests de boîte noire, c'est-à-dire: diviser chaque module X en X et X-test. Seul X a un module-info.Java, les tests s'exécutent dans le chemin de classe, donc vous ignorez tous ces problèmes.

Les seuls inconvénients auxquels je peux penser sont (par ordre d'importance croissante):

  1. Que le pot de test ne sera pas modularisé, mais je pense que c'est acceptable (du moins pour l'instant).
  2. Que vous avez deux fois plus de modules maven et que vous n'aimerez peut-être pas séparer les tests dans un autre module maven.
  3. Les tests s'exécuteront dans le chemin de classe, et il est toujours mauvais d'exécuter des tests dans un environnement différent (les tests s'exécuteront avec moins de restrictions que le code principal). Cela pourrait peut-être être atténué par des tests de fumée ou par un module de tests d'intégration dédié? Les "schémas officiels" ne sont pas encore apparus.

Au fait (si c'est ce que vous faites), je doute que cela vaille la peine de se modulariser, par exemple. une application de démarrage de printemps car le printemps lui-même n'est pas encore modulaire, vous en paierez donc le coût mais en retirerez quelques avantages (mais si vous écrivez une bibliothèque, c'est une autre histoire).

Vous pouvez trouver un exemple ici:

a/Obtenez l'exemple:

git clone https://github.com/vandekeiser/ddd-metamodel.git

git checkout stackoverflow

b/Regardez l'exemple:

  1. fr.cla.ddd.metamodel dépend de fr.cla.ddd.oo (mais pas d'informations sur les modules pour les modules de tests maven)
  2. Je fais des tests de whitebox dans PackagePrivateOoTest et dans PackagePrivateMetamodelTest
  3. J'ai une dépendance de test OoTestDependency
0
vandekeizer