JUnit4を使ってみました(今さら・・・)
僕が始めて触ったテスト用のフレームワークはJUnit3でした。
テストコードを書く楽しさ、重要性、そしてそのコストとそれに見合うだけの品質を得られるか、当時は夢中でテストを結構書きました。
テストを書けば、もしメインのプログラムに修正が発生した場合、テストコードも修正になる。単純に工数が若干あがります。でも、そのかわり、勇気を振り絞らなくても、public系の関数を修正できたりします。
でも、テストは非常に難しいです。おそらく、メインのプログラムを書くより、テストのプログラムを書くほうが難しいのではないかと思うくらい。
まぁ、話はそれましたが、JUnit3を5年近く使ってきて、今度の手前のプロジェクトで初めてJUnit4を触ってみました。本当に、本当に今さら感が漂いますが、ちょっとだけ比較したいと思います。
テストの記述がアノテーションになったよ
Junit3では、TestCaseというクラスを継承したサブクラスを使って、かつテストする関数名はtestから始まる必要がありました(はず)。Junit4からはアノテーションを記述します。ですので、関数名は自由につけれます。
junit3の場合
public class FileTest extends TestCase{ public void testファイルが存在するかな() { fail(); } }
Junit4の場合
import static junit.framework.Assert.*; public class FileTest @Test public void ファイルが存在するかな() { fail(); } }
こんな感じです。まぁ、便利になったと思います。ただ、assert系の関数はあらかじめインポートしておく必要があります。
例外がテストできるようになったよ
こういう書き方をすると、JUnit3が例外のテストができなかった印象をもたれるかもしれませんが、JUnit4では例外を例外のテストすることができるようになりました。
JUnit3の場合
public class FileTest extends TestCase{ public void testファイルが存在するかな() { Hoge hoge = new Hoge(); try { // aruyoはファイルが存在しない場合は、FileNotFoundExceptionを送出する hoge.aruyo(new File("hogehoge")); fail(); } catch (FileNotFoundException e) { assertTrue(true); } } }
Junit4の場合
public class FileTest @Test(expected = FileNotFoundException.class) public void ファイルが存在するかな() { Hoge hoge = new Hoge(); hoge.aruyo(new File("hogehoge")); } }
僕の場合、こんな感じです。確かに例外のテストはコード量は減りましたね。これは便利です。
テストの前に一度だけ実行機能がついたよ
JUnit3はテスト実行前にしておきたい処理はTestCase#setUp関数をオーバーライドすればよいのですが、これはtest関数が実行されるたびに、setUp関数が呼ばれますので、たとえばテストクラスにtest関数が100個あったら、setUp関数が100回呼ばれます。たとえば、httpのテストをしたくて、テストの際Jettyを立ち上げる場合、setUpに起動、tearDownに終了を記載すると、100回起動・終了が行われ、テスト時間を多く裂いてしまいます。じゃ、手前の環境でjettyなりtomネコなど立ち上げておけばいいじゃん、ボケ。といわれそうですが、テストはどんな環境でもテスト結果が同じことが大事だと思うし、極論を言えば100人がプロジェクトをcheckoutしてプロジェクトルートのTestSuite実行して、環境の違い等でテスト結果に差異がでると、ちょっとテストとしては不完全なのかなーと思うのです(現実的に難しいですが、これを目指そうという心構え)
JUnit4だと、アノテーションで@BeforeClassと@AfterClassを記述することで、テスト実行前の初期化が行えます。
Junit4の場合
import static junit.framework.Assert. public class FileTest private static HttpServer server = new HttpServer(); @Test public void 接続できるかな1() { } @Test public void 接続できるかな2() { } @BeforeClass public static void myBeforeClass() throws Exception { server.start(); } @AfterClass public static void myAfterClass() throws Exception { server.stop(); } }
こんな感じで、
myBeforeClass()
↓
接続できるかな1()
↓
接続できるかな2()
↓
myAfterClass()
が実行されます。すべてのテストの実行前に、一度しか実行する必要のない処理については、非常に効率的な書き方をできるようになりました。注意としては、BeforeClass、AfterClassについては、staticで記載しなければならないことです。インスタンス変数を参照できないので、ちょっと面倒なことがありました・・・。
余談として書き方として、もっとも変わったと勝手に思っているのが、TestSuiteの書き方です。
@RunWith(Suite.class) @SuiteClasses({ HogeTest.class, HogeHogeTest.class, HogeHogeHogeTest.class }) public class HogeTestSuite { }
こんな感じ。一度じゃ覚えれないかな・・・。
ちなみに、僕が試した感じだと、このHogeTestSuiteにBeforeClass、AfterClassを記述した場合は、指定してあるTestSuiteの実行前、実行後に呼ばれる感じなので、テーブル作ったりとか、消したりとかは、ここでするとよいかもしれませんが。
豆な知識として、もうデファクトスタンダードかもしれませんが、Eclipseを使う場合、QuickJUnitプラグインを使うとテストが非常に楽に実行できます。
// 最近は別の流行とかあるのかな・・・