JUnit ソースリーディング中...(3)
さすがに理解はできてきたかな。「使える」ようになったかはともかく。
ちょっと整理してみる。自分のために。
JUnitはテキストベースでも使えるし、GUI は awt と swing が使える。それぞれ、
が起動クラスである。つまりこいつらの main が最初に呼び出されることでテストが開始するのだ。これらは junit.runner.BaseTestRunner の子クラスにあたる。
簡単そうな、junit.textui.TestRunner から読むことにした。main の引数にテストクラスをFQCNで指定することで、そのテストクラスが実行され、結果がコンソールに出力されることになる。基本的にこれだけなので、わかりやすい。
ほかの主役はみな、junit.framework パッケージ以下にいる。
- TestCase
- TestSuite
これらは Composite パターンで作られている。つまりファイルとフォルダの関係である。それぞれ Test インターフェースを実装していて run メソッドを持つことが義務づけられている。もし Test の実態が TestCase なら実際にテストメソッドが呼び出されるし、TestSuite ならこいつが持っている TestCase を順に実行していくということになる。
テストクラスは TestCase を extends して書くことからもわかるとおり、これらはテストそのものを管理しているクラスだ。役割としては「実行されたら、setup->テストメソッド->tearDown の順に実行する(ちなみにここは TemplateMethod パターンが適用されている)。例外やエラーが発生したら上に投げる」ということをしてさえいればよろしい。失敗したからどう成功したからどうとかは考えない。「結果をどうユーザに伝えるかという表現」は別のクラスに任せている。
次に、
- TestResult
こいつはテスト結果収集屋さんである。Kent Beck に言わせれば「Collecting Parameter」である。収集というくらいなので、実行する際の全てのテストの結果を保持することになる。TestRunner からテスト実行される際に、ひとつ new して作られて、Test オブジェクト(TestCase とか TestSuite とか)の run メソッドに引数として渡されている。このインスタンスが収集することになる。一回の起動ではTestResultのインスタンスは1つだけである。
こいつの役割はあくまでテスト結果の収集だ。テストが何回実行されて、そのうち失敗(assert 失敗)が何件で、エラーが何件かを管理している。
TestRunner クラスの中では doRun の中で
suite.run(result);
してるかと思えば、呼ばれた TestCase クラスの run メソッドの中では、
result.run(this);
と実行の主体がスイッチしてしまっている。
なんとも難解であるが、TestResultの別の役割を理解すれば納得できる。
TestResult はテスト結果を管理する以外にも「テスト状況を TestListener に伝える」という役割も持っており「テスト開始」、「テスト結果」、「テスト終了」をそのタイミングでTestListenerに通知している。逆に言うとこれらを通知しようと思うと、こいつがTestCaseを実行するしかないということである。
この「通知」というところを、掘り下げてみる。
- TestListener
TestListener インタフェースの中身は以下のとおりである。
package junit.framework; /** * A Listener for test progress */ public interface TestListener { /** * An error occurred. */ public void addError(Test test, Throwable t); /** * A failure occurred. */ public void addFailure(Test test, AssertionFailedError t); /** * A test ended. */ public void endTest(Test test); /** * A test started. */ public void startTest(Test test); }
TestResult が TestCase を実行する際に、
-
- テスト呼ぶ直前には、Listener の startTest をよび、
- テストが失敗(assert失敗)したら、Listener のaddFailure をよび、
- テストが例外をおこしたら、Listener の addError をよび、
- テストが終わったら、Listener の endTest をよぶ
ということをしているのだ。いわゆるObserverパターンである。
実は BaseTestRunner も TestListener を実装しているのだが、textui ではあまり気にしなくていい。textui の場合、TextResult に addListener されているのは、textui パッケージにある、ResultPrinter である。
TestRunnerの中で、上記コードの
suite.run(result);
の直前に、
result.addListener(fPrinter);
と、ResultPrinter がリスナーとして追加されている。
こいつが結果をこまかく受け取って「結果出力」する本体である。textui はその名の通り GUI がないので、コンソールに出力している。その出力の主体である。もっというとその中身は System.out である。
実は、肝はこれで終わりかなと。
あとは出力先が、awt になったり、swing になったりと。
その気になれば、Listener を実装したこだわりのクラスを作りさえすれば、テスト開始や終了、エラー発生の通知を受けて、音を出したり、ランプをつけたりもできるでしょうってことでしょう。たぶん。
さて、つぎは awtui.TestRunner でも見てみますかのう。