JUnit4 を作ってみた
Qiita に Junit を作る記事 はあったが、アノテーションを使っていなかったので、今回はアノテーションを使った JUnit を自作してみた。
参考にしたコードは、junit4/src/main/java/org/ 以下です。成果物は、
にあります。
簡単に使い方を説明すると、以下のようなファイルを作り、実行すると、
import myjunit4.JUnitCore;
import myjunit4.Test;
import myjunit4.Before;
import myjunit4.After;
import myjunit4.Assertion;
public class SampleTest1 {
private int hoge;
public static void main (String[] args) {
JUnitCore.main(SampleTest1.class.getName());
}
@Before
public void beforeTest() {
System.out.println("Test Start...");
hoge = 0;
}
@After
public void afterTest() {
System.out.println("Test Finished...\n");
}
@Test
public void test1() {
Assertion.assertEquals("Should be equal.", 0, hoge);
}
@Test
public void test2() {
Assertion.assertEquals("Should not be equal.", 1, hoge);
}
}
Before -> Test -> After の順番で、メソッドが実行される流れになっている。
それでは中身を見てみましょう。
自作 JUnit の中身
Before, Test, After の実体はアノテーションを普通に定義しているだけのものになっています。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Test {}
ここはそこまで難しくはありませんね。Before と After も同じ感じです。
問題は、JUnitCore.main(クラス名)
の部分ですね。
ここでは最初に、クラスのオブジェクトを forName
で取得(ここらへん)して、それに定義されているメソッドに対して Testアノテーション・Beforeアノテーション・Afterアノテーション が定義されているメソッドを配列に登録します。
Test testAnnotation = method.getAnnotation(Test.class);
Before beforeAnnotation = method.getAnnotation(Before.class);
After afterAnnotation = method.getAnnotation(After.class);
if ( testAnnotation == null && beforeAnnotation == null && afterAnnotation == null ) continue;
+ if ( testAnnotation != null ) testMethods.add(method);
+ if ( beforeAnnotation != null ) beforeMethods.add(method);
+ if ( afterAnnotation != null ) afterMethods.add(method);
そして、それを使って、Statement という抽象クラスを継承した TestStatement, BeforeStatement, AfterStatement クラスを作り、それぞれで定義された evaluate()
を実行していきます。
以下が、TestStatement の例です。
package myjunit4;
import java.util.List;
import java.util.ArrayList;
import java.lang.reflect.Method;
import myjunit4.Statement;
import myjunit4.Counter;
public class TestStatement extends Statement {
private List<Method> testMethods = new ArrayList<Method>();
private Object target;
private Counter counter;
public TestStatement(List<Method> testMethods, Object target, Counter counter) {
this.testMethods = testMethods;
this.target = target;
this.counter = counter;
}
+ @Override
+ public void evaluate() throws Throwable {
+ for ( Method method : testMethods ) {
+ counter.addCount();
+ try {
+ method.invoke(target);
+ } catch (Throwable e) {
+ counter.addFailure();
+ }
+ }
+ }
}
これに、Counter クラスで、実行したテストの総数やエラーの数を数えています。
ここまでで実装を見てみて如何だったでしょうか?意外と簡単だったのではないでしょうか?
本家のコードを参考にした時は、意外と簡単に出来ていて自分も驚きました。
今後、JUnit を使う時は、JUnit の気持ちにより近づいて実装ができるようになれれば嬉しいです。
暇があったら、JUnit4 の本家のコードの解説もしたいと思います。