web-dev-qa-db-ja.com

Javaモジュール間の適切な実装、Mavenビルドでのモジュール間テストの依存関係によるビルド

MavenとJavaを使用したマルチモジュールプロジェクトがあります。 Java 9/10/11に移行してモジュールを実装しようとしています JSR 376:Java Platform Module System)のように =、JPMS)。プロジェクトはすでにMavenモジュールで構成されており、依存関係は単純であったため、プロジェクトのモジュール記述子の作成は非常に簡単でした。

各Mavenモジュールの独自のモジュール記述子(module-info.Java)がsrc/main/Javaフォルダーに追加されました。テストクラスのモジュール記述子はありません。

しかし、解決できなかった問題に遭遇し、解決方法の説明が見つかりませんでした。

モジュール間test MavenおよびJavaモジュール?との依存関係をどうやって持つことができますか?

私の場合、「共通」のMavenモジュールがあり、これにはいくつかのインターフェースおよび/または抽象クラスが含まれています(ただし、具体的な実装はありません)。同じMavenモジュールで、これらのインターフェース/抽象クラスの実装の適切な動作を確認するabstractテストがあります。次に、1つ以上のサブモジュールがあり、インターフェース/抽象クラスの実装と抽象テストを拡張するテストがあります。

ただし、Mavenビルドのtestフェーズを実行しようとすると、サブモジュールは次のエラーで失敗します:

[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

テストはモジュールの一部ではないが原因であると考えられます。また、Mavenがモジュールのスコープ内でテストを実行するために何らかの「魔法」をかけても、(何らかの理由で)私が依存しているモジュールのテストでは機能しません。どうすれば修正できますか?

プロジェクトの構造は次のようになります( ここで利用可能な完全なデモプロジェクトファイル ):

├───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

my-impl-module/pom.xmlの依存関係は次のとおりです。

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

注:上記は、問題を実証するために作成したプロジェクトです。実際のプロジェクトははるかに複雑で、 ここにあります (マスターブランチはnotモジュール化されています)ですが、原理は同じです。

PS:すべてが通常のクラスパスを使用してコンパイルおよび実行されるため(つまり、IntelliJ、またはJavaモジュール記述子なしのMaven)、すべてがコンパイルおよび実行されるため、コード自体に問題はないと思います。問題Javaモジュールとモジュールパスで紹介されています。

20
haraldK

あなたのデモプロジェクトに基づいて、私はあなたのエラーを複製することができました。とはいえ、プロジェクトをビルドできるようにするために、最初に失敗した後のrevised変更は次のとおりです。

  1. すべてのモジュールにmaven-compiler-pluginバージョン3.8.0を追加しました。 Mavenでモジュールをコンパイルするには、3.7以降のバージョンが必要です。少なくとも、これはNetBeansが示した警告です。害はないので、commonimplementationモジュールの両方のPOMファイルにプラグインを追加しました。

    <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. テストクラスを独自のjarファイルにエクスポートしたので、実装モジュールなどの誰でも利用できるようになります。そのためには、my-common-module/pom.xmlファイルに以下を追加する必要があります。

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

    これにより、my-common-moduleテストクラスが-tests.jarファイルにエクスポートされます(例:my-common-module-1.0-SNAPSHOT-tests.jar)。この post に示されているように、通常のjarファイルの実行を追加する必要がないことに注意してください。ただし、これにより、次に対処するエラーが発生します。

  3. 実装テストクラスをコンパイルするときにテストクラスがロードされるように、テストパッケージの名前をmy-common-moduleからcom.example.common.testに変更します。これにより、最初のjar、この場合はモジュールがロードされ、2番目のjar、テストjarファイルは無視されます。興味深いことに、観察に基づいて、Mavenコンパイルパラメーターがクラスパスの最初にtests.jarが指定されていることを示しているため、モジュールパスの方がクラスパスよりも優先順位が高いと結論付けています。 mvn clean validate test -Xを実行すると、コンパイルパラメータが表示されます。

    -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. エクスポートされたテストクラスを実装モジュールで利用できるようにする必要があります。この依存関係を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. 最後にmy-impl-moduleテストクラスで、インポートを更新して新しいテストパッケージcom.example.common.textを指定し、my-common-moduleテストクラスにアクセスします。

    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 { ... }
    

新しい変更のmvn clean packageのテスト結果は次のとおりです。

enter image description here

Java-cross-module-testing GitHubリポジトリのサンプルコードを更新しました。私が唯一残している質問は、あなたもそうだと思いますが、実装モジュールをモジュールではなく通常のjarプロジェクトとして定義したときに、なぜうまくいったのかです。しかし、それは、別の日に遊んでみます。うまくいけば、私が提供したものがあなたの問題を解決します。

6
Jose Henriquez

私はまったく同じようにしようとしましたが、ホワイトボックステストとモジュールテストの依存関係の両方を持つことは不可能ですプロジェクトの構造を使用して行う:

1 /ホワイトボックステストの問題は、JPMSがMavenとは異なりテストVSメインの概念を持たないため、モジュールパッチで機能することです。したがって、これはテスト依存関係で動作しない、またはテスト依存関係でモジュール情報を汚染する必要があるなどの問題を引き起こします。

2 /では、ホワイトボックステストを続けないでください。ただし、ブラックボックステストのmaven構造を使用して、つまり、各モジュールXをXとXテストに分割します。 Xのみにmodule-info.Javaがあり、テストはクラスパスで実行されるため、これらの問題はすべてスキップします。

私が考えることができる唯一の欠点は(重要度が高い順に)です:

  1. テストjarはモジュール化されませんが、それは許容できると思います(少なくとも今のところ)。
  2. Mavenモジュールの数が2倍になり、別のMavenモジュールでテストを分離したくない場合があります。
  3. テストはクラスパスで実行され、別の環境でテストを実行することは常に悪いことです(テストはメインコードよりも制限なしで実行されます)。これは、たぶんスモークテストまたは専用の統合テストモジュールによって緩和される可能性がありますか? 「公式パターン」はまだ現れていません。

ちなみに(それがあなたがやっていることなら)、モジュール化する価値はないと思います。スプリング自体はまだモジュール化されていないので、スプリングブートアプリはコストを支払いますが、いくつかの利点を享受します(ただし、別の話であるlibを作成している場合)。

ここで例を見つけることができます:

a /例を取得:

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

git checkout stackoverflow

b /例を見てください:

  1. fr.cla.ddd.metamodelはfr.cla.ddd.ooに依存します(ただし、テスト用のmavenモジュールのmodule-infoはありません)
  2. PackagePrivateOoTestおよびPackagePrivateMetamodelTestでホワイトボックステストを行います
  3. 私はテストの能力を持っていますOoTestDependency
0
vandekeizer