ユニットテストを気軽にわかりやすく書くための一提案

これまでに公開しているオープンソースのコードを見て貰えばわかるのだが、ここに付属しているユニットテストは通常「こうやれ」というやり方では記述していない。そんな必要は無いし、この方が便利だと思うからである。これについて解説する。

皆さんが従っている馬鹿馬鹿しい「ディレクトリ分割作法」について

ソースディレクトリについては、以下のような構成が「良い」とされているようだ。これはMavenビルドシステムから始まっているものと思うが、私には定かではない。

src
+- main
   +- java
      +- com
         +- cm55
   +- resources
+- test
   +- java
       ....

すなわち、基本的には開発対象のJavaソースとユニットテストソースを完全に分離させるのである。つまり、Sample.javaという製品用のクラスと、それをテストするSampleTest.javaは全く異なるディレクトリ階層の中に置かれることになる。

私の方法

ソースを見てもらえばわかるのだが、当方ではこんなことはしない。Sample.javaとSampleTest.javaは「隣どうし」に置かれている。例えば、EventBusのソースを見ていただきたい。以下のように製品用クラスと、それをテストするクラスはおおよそ隣どうしに配置されている。

こうすれば、製品用クラスを修正したときにも、それがどこでテストされているのかがすぐわかるし、すぐ修正できることになる。

そして、同じパッケージ内にあるこれらすべてのテストをAllTestでまとめている。以下のコードだ。

package com.cm55.eventBus;


import org.junit.runner.*;
import org.junit.runners.*;
import org.junit.runners.Suite.*;


@RunWith(Suite.class)
@SuiteClasses( { 
  CallerStackTest.class,
  EventBusTest.class,
  EventBusEventTypeTest.class,
  EventTypeTest.class,
  ListenerHolderTest.class,
  UnlistenerTest.class

})
public class AllTest {
  public static void main(String[] args) {
    JUnitCore.main(AllTest.class.getName());
  }
}

製品からユニットテストを除去する方法

「これでは製品にもユニットテストが残ってしまうんじゃ?」と思うかもしれないが、そんなことは無い。なぜならソースセットを以下のように指定しているからだ(build.gradle)。

sourceSets {
  main {
    java {
      srcDir 'src'; exclude '**/*Test.java'
    }
    resources {
      srcDir 'src'; exclude '**/*Test.java'
    }
  }
}

ビルド対象からすべての*Test.javaを除去してしまう。唯一の問題と言えば、「なんちゃらTest」というクラスは作れないことのみだ。ソースコードに関しては、これ以外の問題は無い。

Eclipseの環境設定

唯一問題となるのは、Gradleのeclipseプラグイン(EclipseのGradleプラグインではない)を使用して、build.gradleのsourceSetsをEclipseの.classpathファイルを自動生成させていることだ。つまり、sourceSetsに記述した内容をEclipseのJava Build PathのSource部分に記述されるのだが、このとき*Test.javaというファイルが除外されてしまい、そのままではEclipse上でも*Test.javaファイルがJavaソースとして認識されない。

※上は非常にややこしい記述だが、これ以外に書きようがない。

これを避けるためには、build.gradleに仕掛けを施しておく必要がある。これは既にEclipseClasspathの使い方に記述してあるので参照されたい。