JUnit 4.1 で学ぶ JavaSE 5.0
※ここに記載しているソースコードは、JUnitから転載したものです。
概要
JUnit4.1のソース解析にチャレンジしてみようと思います。これによって、デザインパターン、JavaSE5.0のプログラミング手法を習得してしまおうという試みです。
JUnit概要
JUnitは言わずと知れたJavaのテストツールです。ユニットテストを簡単に行うためのライブラリのようなものです。JUnitの作者は、Javaの世界では有名な方々。きっと参考になるはず!!というわけでチャレンジです。
ソースコードの取得
ソースコードは、ZIPになっているものを探しましたが見当たらず・・・。結局、sourceForge.netの公開されているCVSへanoymouseで接続し、junit4.1のソースコードをチェックアウトしてきました。
まずは、サンプルで動作確認
ソース解析のとっかかりとして、一番良いのは動作させてみることでしょう。というわけで、ソースの中からサンプルらしきクラスを探します。
見つけたのは、ListTest.javaというクラス。どうやら簡単そうです。とっかかりには十分です。
package org.junit.samples; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.List; import junit.framework.JUnit4TestAdapter; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; /** * A sample test case, testing <code>java.util.Vector</code>. * */ public class ListTest { protected List<Integer> fEmpty; protected List<Integer> fFull; protected static List<Integer> fgHeavy; public static void main (String... args) { junit.textui.TestRunner.run (suite()); } @BeforeClass public static void setUpOnce() { fgHeavy= new ArrayList<Integer>(); for(int i= 0; i < 1000; i++) fgHeavy.add(i); } @Before public void setUp() { fEmpty= new ArrayList<Integer>(); fFull= new ArrayList<Integer>(); fFull.add(1); fFull.add(2); fFull.add(3); } public static junit.framework.Test suite() { return new JUnit4TestAdapter(ListTest.class); } @Ignore("not today") @Test public void capacity() { int size= fFull.size(); for (int i= 0; i < 100; i++) fFull.add(i); assertTrue(fFull.size() == 100+size); } @Test public void testCopy() { List<Integer> copy= new ArrayList<Integer>(fFull.size()); copy.addAll(fFull); assertTrue(copy.size() == fFull.size()); assertTrue(copy.contains(1)); } @Test public void contains() { assertTrue(fFull.contains(1)); assertTrue(!fEmpty.contains(1)); } @Test (expected=IndexOutOfBoundsException.class) public void elementAt() { int i= fFull.get(0); assertTrue(i == 1); fFull.get(fFull.size()); // Should throw IndexOutOfBoundsException } @Test public void removeAll() { fFull.removeAll(fFull); fEmpty.removeAll(fEmpty); assertTrue(fFull.isEmpty()); assertTrue(fEmpty.isEmpty()); } @Test public void removeElement() { fFull.remove(new Integer(3)); assertTrue(!fFull.contains(3)); } }
このソースを実行してみましょう。
見てわかるとおり、5つあるテストのうち、4つしか実行されていないことがわかります。実行されていないのは、capacityメソッドです。他のメソッドと見比べてみると、その原因がわかります。「@Ignore("not today") 」という記述があります。この記述があることで、このメソッドがテストとして実行されなくなります。このようにメソッド等にメタ情報を付加する機能をアノテーションといいます。
annotation アノテーション
アノテーションは、メソッドなどにメタ情報を付加することができる機能です。JUnitでの使用方法を見てみましょう。「Ignore」アノテーションは、テストを実行したくない場合に付けるアノテーションです。定義を見てみましょう。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Ignore { /** * The optional reason why the test is ignored. */ String value() default ""; }
「@Retention(RetentionPolicy.RUNTIME)」は、リフレクションにより取得可能であることを示します。「@Target(ElementType.METHOD)」は、メソッドに対してのアノテーションであることを示します。アノテーションの定義は、インターフェースの定義に似ています。「@interface 」と定義します。
アノテーションの定義には、いくつかの決まりがあります。
* メソッドの宣言には、パラメータ、throw節を設定できない。
* 戻り値の型は、プリミティブ型、String、Class、列挙型、アノテーション、これらの配列のみ。
* デフォルトの値を指定可能。「default」と記載。
* 要素は、省略可能。その場合は、マーカー注釈型と呼ばれる。この場合、()の省略も可能
* 要素が単一の場合は、「value」という要素にしなければいけない。この場合、呼び出し側で「=」の記述を省略可能
Ignoreアノテーションの読み込み
では、Ignoreアノテーションは、どのようにして読み取られるのでしょうか?利用する側を見てみましょう。
調べてみると、TestIntrospectorクラスで読み取られていることがわかります。コードは次のとおりです。
public class TestIntrospector { private final Class<?> fTestClass; // ・・・中略 public boolean isIgnored(Method eachMethod) { return eachMethod.getAnnotation(Ignore.class) != null; } // ・・・