Eclipse:Java9モジュールシステムを使うと共にJUnitを使う方法

Eclipseで一応Java9モジュールシステムの開発をしてみようと思ったのだが、そこではたと気がついた。JUnitはどうすれば良いのだろうか?

以下ではEclipse 2019-06を対象とする。

問題

前提として以下がある。

  • Eclipseでは、一つのプロジェクトは一つのモジュールにしかできない。module-info.javaは一つだけ。
  • プロジェクト内にJUnitコードを含めたい。わざわざ別プロジェクトとしたくはない。
  • しかしそうしてしまうと、module-info.javaにjunitが出現せざるをえなくなる。
  • したがって、リリース時のmodule-info.classにもjunitが含まれてしまう。
  • つまり、実行時にjunitが必要ということになってしまう。

具体的にどんな状況になるかだが、以下を作成してみる。

本体ソースはsrc下に、テストコードはtestというソースフォルダを作成して、その中に入れる。ソースは以下のような具合だ。

Main.java

package sample;
import java.sql.*;
import javax.swing.*;
public class Main {
  public Main() {
    Connection a;
    JOptionPane b;
  }
}

MainTest.java

package sample;

import org.junit.*;
public class MainTest {
  @Test
  public void test() {    
  }
}

そして、module-info.javaは

module sample {
  requires java.sql;
  requires java.desktop;
  requires junit;
}

ライブラリは以下になっている。

この状態では、何をどうやろうが、module-info.javaからjunitをはずすことはできない。junitが正規の形で「このモジュール(sample)が必要とするもの」となってしまっているからだ。

解決策

これについての正式な文書や議論を見つけ出せないのだが、以下のようにすれば良いようだ。

  • testソースフォルダを「テスト用」とマークし、コンパイル済バイナリの出力先を本体とは別にする。
  • JUnitライブラリをモジュールパスではなく、クラスパスに入れる。
  • module-info.javaからjunitを削除する

以下これをやってみる。

testソースフォルダを「テスト用」とマークする

ビルドパスを開くと以下になっているので、

「Contains test sources: No」になっているのをダブルクリックしてYesに変更する。

コンパイル済バイナリの出力先を本体とは別にする

上部に警告がでており、「テスト用にしたら別の出力先にしたいとだめだよ」と言っている。

そこで、下の「Allow output folders for source folders」をチェックし、sample/testのOutput folderをダブルクリックする。

このように適当な出力先を割り当てる。

Junitをクラスパスに入れる

モジュールパスにいれていたJUnitをクラスパスの方に変更する。

この時点でmodule-info.javaにエラーが発生していることがわかる。

module-info.javaからjunitを削除する

以下のエラーが発生しているので、junitを削除する。

.projectの状態

この単純な変更で生成された.projectは以下だ。

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
  <classpathentry kind="src" path="src"/>
  <classpathentry kind="src" output="testout" path="test"> <!-- デフォルトとは別出力先 -->
    <attributes>
      <attribute name="test" value="true"/> <!-- テスト用ソースを示す -->
    </attributes>
  </classpathentry>
  <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
  <attributes>
    <attribute name="module" value="true"/> <!--モジュールパスにあることを示す -->
  </attributes>
  </classpathentry>
  <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
                                            <!-- クラスパスにあることを示す -->
  <classpathentry kind="output" path="bin"/> <!-- デフォルトの出力先 -->
</classpath>

まとめ

つまり、こういうことらしい。

  • テストソースの出力先を本体とは別にすることにより、モジュールシステムの支配下に置かない
  • JUnitをクラスパスに置くことにより、モジュールシステムの支配下に置かない。
  • したがって、module-info.javaに書く必要なし。
  • 各ユニットテストは、「クラスパスシステム」の下で動作する。

ということだ。