Junit4 を自作してみた

## JUnit4 を作ってみた 

Qiita に [Junit を作る記事](https://qiita.com/torutakefusa/items/1cc7cba5842a5128b9bc) はあったが、アノテーションを使っていなかったので、今回はアノテーションを使った JUnit を自作してみた。

参考にしたコードは、[junit4/src/main/java/org/](https://github.com/junit-team/junit4/tree/9ad61c6bf757be8d8968fd5977ab3ae15b0c5aba/src/main/java/org/junit) 以下です。成果物は、

https://github.com/YmBIgo/my_junit4

にあります。

簡単に使い方を説明すると、以下のようなファイルを作り、実行すると、

```java:SampleTest1.java
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 の実体はアノテーションを普通に定義しているだけのものになっています。

```java:Test.java
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` で取得（[ここらへん](https://github.com/YmBIgo/my_junit4/blob/f6155d9f0e1b3e6342fc16b35f8a5a270afc4e14/myjunit4/JUnitCore.java#L38-L48)）して、それに定義されているメソッドに対して Testアノテーション・Beforeアノテーション・Afterアノテーション が定義されているメソッドを配列に登録します。

```diff_java:JUnitCore.java 
			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 の例です。

```diff_java:TestStatement.java
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 の本家のコードの解説もしたいと思います。