JUnit4.13のソースを追っかけてみた
先日参加したSQiP「キーワード駆動テストシステムを作ろう」で学んだ、ランナーの役割をJUnitでも実践できるのかを検証する為に、JUnit4のソースコードを読んでみました。
作ったクラス図
折角、なんちゃってクラス図を書いたので公開します。
参考にしたサイト
最初は「ランナーってなんだ?」というところから入って、こちらのサイトを参考にし始めたのですが、分かったような分からないようなままになっていました。
もやもやしたまま、SQiPのチュートリアルを迎えて、ランナーの役割が分かったところで、JUnitのソースを読むと不思議と頭に入ってきました。
途中、こちらのサイトも参考にさせて頂いたり
ihirokyさんは、super.runChild()をオーバーライドしたrunChild()の中で呼び出しています。これも一つの方法ですよね。
BlockJUnit4ClassRunnerを継承したカスタムランナーなので、必ずBefore/Test/Afterの間に実行されるようになります。
また、こちらのサイトを参考にさせて頂いたりしました。
渡辺さんのカスタムランナーは記事でも書かれているように、ユニットテスト以外のテストにも利用されています。なので、run(notifier)からinvokeTest(notifier, testcase)を実行するような作りでカスタムランナーを作られています。面白いです。
結局何がいいのかというと、JUnitを使ってどんな風に「テストを実行したいか」が重要なのかなと思いました。
渡辺さんも書かれていますが、JUnitは「テストケースがクラス毎にメソッド単位で定義されている」というところが原則です。ですから、この考え方に沿ってJUnitを利用するのが、自然な使い方かなと僕も思います。
僕がカスタムランナーを作るとしたら
自分が作っているキーワード駆動テストシステムで共通した画面操作を共通処理に移すにはこうすると思います。
例えば、毎回ログインログアウトが必要なシステムだとします。
これをいちいちテストに書くのは面倒なので、共通処理化してしまいます。
案1)
・カスタムランナークラスの定義
public Class LoginTestLogoutRunner extends BlockJUnit4ClassRunner
・methodInvokerのオーバーライド
@Override protected Statement methodInvoker(...) {
return new CustomInvokeMethod(method, test);
}
・@Testを実行するInvokeMethodのを継承したカスタムInvokeMethodの定義
public class CustomInvokeMethod extends InvokeMethod
案2)
もしくは、JUnitのStatementクラスが「Chain of Responsibility」パターンを利用している事を利用して、Statementのサブクラスを追加しちゃうのも面白いかもしれませんね。
@Testでは、キーワードスクリプトが処理をして、その前後で共通処理を実装できればよいので。
そうすると、テストクラスはシナリオのパターン分できますが、@Testの前後で共通の処理を固定的に実行することが出来ますね。
案3)
@Ruleとして必ず実行させるような仕掛けにしてしまうのも、ありかもしれません。
9/25追記
案4)
カスタムランナーを作らずに済ませる簡単な方法は、@Testに共通処理を書いてしまって、意図した順番で実行する―というのも、一つの手かと思いました。
これなら1クラス=1テストケースとして取り扱うことが出来ますね。
渡辺さんが仰っているように、JUnitの構造上は「1メソッド=1テストケース」ですから、この点を気にしなければこの方法が一番簡単かと思います。
自動でテストを流す時に、実行するクラスが増えますが、Suiteランナーで実行するクラスをまとめてあげれば問題ないと思いました。
まとめ
いづれにしてもJUnitはまだまだ奥が深いし、これを使ってやりたいことはまだたくさんあります。内容を紐解いて自分がやりたいことを深堀した結果を公開したいと思います。