web-dev-qa-db-fra.com

Java 9 + maven + junit: le code de test a-t-il besoin de module-info.Java propre et où le mettre?

Disons que j'ai un projet Java utilisant Maven 3 et junit. Il y a src/main/Java et src/test/Java répertoires contenant respectivement les sources principales et les sources de test (tout est standard).

Maintenant, je veux migrer le projet vers Java 9. src/main/Java le contenu représente Java 9; il y a com/acme/project/module-info.Java ressemblant approximativement à ceci:

module com.acme.project {
    require module1;
    require module2;
    ...
}

Que faire si le code de test a besoin de module-info.Java de sa propre? Par exemple, pour ajouter une dépendance à un module qui n'est nécessaire que pour les tests, pas pour le code de production. Dans un tel cas, je dois mettre module-info.Java à src/test/Java/com/acme/project/ donnant un nom différent au module. De cette façon, Maven semble traiter les sources principales et les sources de test comme des modules différents, je dois donc exporter des packages du module principal vers le module de test et exiger des packages dans le module de test, quelque chose comme ceci:

module principal (dans src/main/Java/com/acme/project):

module prod.module {
    exports com.acme.project to test.module;
}

module de test (dans src/test/Java/com/acme/project):

module test.module {
    requires junit;
    requires prod.module;
}

Cela produit

[ERROR] Failed to execute goal org.Apache.maven.plugins:maven-compiler-plugin:3.7.0:testCompile (default-testCompile) on project test-Java9-modules-junit: Compilation failure: Compilation failure:
[ERROR] /home/rpuch/git/my/test-Java9-modules-junit/src/test/Java/com/acme/project/GreeterTest.Java:[1,1] package exists in another module: prod.module

car un package est défini dans deux modules. Alors maintenant, je dois avoir différents projets dans le module principal et le module de test, ce qui n'est pas pratique.

Je sens que je me trompe de chemin, tout commence à paraître très moche. Comment puis-je avoir module-info.Java propre dans le code de test, ou comment puis-je obtenir les mêmes effets (require, etc.) sans lui?

29

Le système de modules ne fait pas de distinction entre le code de production et le code de test, donc si vous choisissez de modulariser le code de test, le prod.module et le test.module ne peut pas partager le même package com.acme.project, comme décrit dans le spécifications :

Non-interférence - Le Java, machine virtuelle et système d'exécution doit garantir que les modules qui contiennent des packages du même nom n'interfèrent pas. Si deux modules distincts contiennent des packages du même nom, du point de vue de chaque module, tous les types et membres de ce package sont définis uniquement par ce module. Code dans ce package dans un module ne doit pas être en mesure d'accéder aux types de packages privés ou aux membres de ce package dans l'autre module.

Comme indiqué par Alan Bateman, le plugin du compilateur Maven utilise - patch-module et autres options fourni par le système de modules lors de la compilation du code dans l'arborescence src/test/Java, de sorte que le module testé est augmenté avec les classes de test. Et cela est également fait par le plugin Surefire lors de l'exécution des classes de test (voir Prise en charge de l'exécution de tests unitaires dans nommé Java 9 modules ). Cela signifie que vous n'avez pas besoin pour placer votre code de test dans un module.

8
manouti

Vous voudrez peut-être repenser la conception du projet que vous essayez d'implémenter . Puisque vous implémentez un module et son test dans un projet, vous devez vous abstenir d'utiliser différents modules pour chacun d'eux individuellement.

Il ne devrait y avoir qu'un seul module-info.Java pour un module et ses tests correspondants.

La structure de votre projet peut ressembler à ceci: -

Project/
|-- pom.xml/
|
|-- src/
|   |-- test/
|   |   |-- com.acme.project
|   |   |        |-- com/acme/project
|   |   |        |      |-- SomeTest.Java
|   |   
|   |-- main/
|   |   |-- com.acme.project
|   |   |    |-- module-info.Java
|   |   |    |-- com/acme/project
|   |   |    |    |-- Main.Java

où le module-info.Java pourrait en outre être: -

module com.acme.project {
    requires module1;
    requires module2;
    // requires junit; not required using Maven
}

Pour résumer tout ce qui précède selon vos questions -

Je sens que je me trompe de chemin, tout commence à paraître très moche. Comment puis-je avoir son propre module-info.Java dans le code de test, ou comment obtenir les mêmes effets (requis, etc.) sans lui?

Oui , vous ne devriez pas envisager de gérer différents modules pour le code de test ce qui le rend complexe.

Vous pouvez obtenir un effet similaire en traitant junit comme dépendance à la compilation en utilisant les directives comme suit-

requires static junit;

En utilisant Maven, vous pouvez y parvenir en suivant la structure indiquée ci-dessus et en utilisant maven-surefire-plugin qui se chargerait de patcher les tests sur le module lui-même.

7
Naman

Ajout de quelques détails.

Dans Java depuis 9, un fichier jar (ou un répertoire avec des classes) peut être placé sur classpath (comme précédemment), ou sur module path. S'il est ajouté à classpath, son module-info est ignoré et aucune restriction liée au module (ce qui lit quoi, ce qui exporte quoi, etc.) n'est appliquée. Si, toutefois, un fichier jar est ajouté au chemin du module, il est traité comme un module, donc ses informations sur le module sont traitées, et des restrictions supplémentaires liées au module seront appliquées.

Actuellement (version 2.20.1), maven-surefire-plugin ne peut fonctionner qu'à l'ancienne, donc il place les classes testées sur classpath, et module-path est ignoré. Donc, pour le moment, l'ajout d'informations sur le module à un projet Maven ne devrait rien changer aux tests exécutés à l'aide de Maven (avec le plugin surefire).

Dans mon cas, la ligne de commande est la suivante:

/bin/sh -c cd /home/rpuch/git/my/test-Java9-modules-junit && /home/rpuch/soft/jdk-9/bin/Java --add-modules Java.se.ee -jar /home/rpuch/git/my/test-Java9-modules-junit/target/surefire/surefirebooter852849097737067355.jar /home/rpuch/git/my/test-Java9-modules-junit/target/surefire 2017-10-12T23-09-21_577-jvmRun1 surefire8407763413259855828tmp surefire_05575863484264768860tmp

Les classes testées ne sont pas ajoutées en tant que module, elles sont donc sur classpath.

Actuellement, un travail est en cours dans https://issues.Apache.org/jira/browse/SUREFIRE-1262 (SUREFIRE-1420 est marqué comme un doublon de SUREFIRE-1262) pour enseigner au plugin surefire pour mettre le code sous test sur le chemin du module. Une fois terminé et publié, un module-info sera considéré. Mais s'ils font que le module sous test lise automatiquement le module junit (comme le suggère SUREFIRE-1420), module-info (qui est un descripteur de module principal) n'aura pas à inclure une référence à junit (qui n'est nécessaire que pour les tests) .

Un CV:

  1. module-info doit juste être ajouté aux sources principales
  2. pour le moment, surefire ignore la nouvelle logique liée au module (mais cela sera changé à l'avenir)
  3. (quand les modules fonctionneront sous des tests infaillibles) junit n'aura probablement pas besoin d'être ajouté à l'info-module
  4. (lorsque les modules fonctionneront sous des tests infaillibles) si un module est requis par les tests (et uniquement par eux), il peut être ajouté en tant que dépendance de compilation uniquement (en utilisant require static), comme suggéré par @nullpointer. Dans ce cas, le module Maven devra dépendre d'un artefact fournissant ce module de test uniquement en utilisant la portée de compilation (pas de test) que je n'aime pas beaucoup.
2
Roman Puchkovskiy