JFCUnit: テストケース記述方法
テストコード基本
#テストケースの書き方、実行の仕組みは JUnitと同様になります。
#JUnitのドキュメントはたくさんありますのでより詳しく知りたい方は探してみるといいと思います。
テストを実行するためには、JFCUnit(jfcunit.jar) , JUnit(junit.jar) , Regexp(jakarta-regexp-1.2.jar) が
必要になります。
Eclipseなどでコードインサイト機能を使用する場合にはクラスパスの設定を行っておくとよいでしょう。
テストケースのコードの記述方法を、Drag&Drop のサンプルソースを参考にしてご説明します。
(サンプルソースのコメントなど、省略している部分があります。ご了承ください)
テストケースは、junit.extensions.jfcunit.JFCTestCase
のクラスを継承して作成します。(12行目)
コンストラクタは String(テスト名称) を引数のものを作成し、継承元のメソッドを実行します。(17~19行目)
setUp() メソッドは、テストの前に実行されます。
つまり、テストの準備を行う部分になります。
上記の例では
・Helper(GUI操作をするメソッドが含まれているもの)を作成 (23行目)
・このテストケースを実行 (24行目)
・画面が起動し終わるまで待つ (25行目)
・「Drag and Drop test」というタイトルの画面の参照を取得
を行っています。
tearDown() メソッドは、テスト後に実行されます。つまり、テストにて使用したオブジェクトの解放処理などを行います。
上記の例では
・Helperを開放
を行っています。
testDragAndDrop() メソッドは 実際のテストロジックになります。
JUnit、JFCUnitでは、メソッド名が test から始まるものを自動的に 「テストケース」と認識し、実行します。
テスト実行時には、test から始まるメソッド単位にクラスがインスタンス化されます。
(コンストラクタのnameにtestメソッドの名称が引数として実行される)
つまり、変数などはtestメソッド単位に初期化されますので、影響はありません。
逆に言うと、他のtestの実行、結果の状態に影響されるテストケースは書くべきではありません。
テストは、setUp() → runTest() → tearDown() にてひとつのテスト(サイクル)になります。
ひとつのTestCase内に複数のtestメソッドがある場合には、setUp(),tearDown() のメソッドは共有し、
runTest()時にtestから始まるメソッドが実行されます。
たとえば、test001 と test002 と2つtestメソッドを作成した場合、
(1)クラスをインスタンス化(コンストラクタの引数に test001 の名称が渡される)
(2)setUp() 実行
(3)test001() 実行
(4)tearDown() 実行
(5)クラスをインスタンス化(コンストラクタの引数に test002 の名称が渡される)
(6)setUp() 実行
(7)test002() 実行
(8)tearDown() 実行
という順序で実行されていきます。
コンポーネント参照取得方法
Swingコンポーネントを動かすには、まず操作対象となるコンポーネントをまず取得する必要があります。
junit.extensions.jfcunit.TestHelper にてコンポーネントを取得するためのメソッドがあります。
■画面を取得する方法
JFrameのインスタンスである画面は
Window frame = TestHelper.getWindow("WindowTitle");
JDialogのインスタンスである画面は
java.util.List dialogs = TestHelper.getShowingDialogs("WindowTitle");
にて画面の参照を取得できます。
■画面内コンポーネントを取得する方法
Component comp = TestHelper.findComponent(Class compCls, Container cont, int index)
Component comp = TestHelper.findNamedComponent(Class aClass, String name, Container cont, int index)
引数が違うメソッドがいくつかありますが、基本的にはこの2つになります。
1つめの findComponent は、第二引数のコンテナの中の第一引数のコンポーネントクラスの中で、第三引数番目のコンポーネントを取得します。
2つめの findNamedComponent は、findComponentに 「名前」パターンを条件に加えたものになります。
何番目・・・というのは、配置方法によって変わってきてしまうので基本的には 「名前」を使用する方がよいかと思います。
この「名前」というのは java.awt.Component#getName() にて返される名前になります。
デフォルトではこの getName() は null になります。「名前」にて取得する場合には、コンポーネント個々に java.awt.Component#setName(String name) の
メソッドにより 名称を設定する必要があります。
また、この「名称」はパターンマッチングの条件になりますので、指定文字が複数コンポーネントに含まれていた場合には 第四引数の番号目の
コンポーネントが返されます。
例えば、2つのコンポーネントに「Title」 と 「DetailTitle」 と 名称をつけた場合、nameの引数を「Title」とすると2つとも対象になります。
●注意
Webtribe/VisualFrameを使用している場合には、ツールにて指定した「コンポーネントの物理名」が自動的に JCompoennt#setName() に
格納されます。
より明示的に取得が可能ですので、名称を設定する必要はありません。取得も上記メソッドを使う必要はありません。
コンポーネント動作方法
コンポーネントに対し、Key入力、マウス操作 のユーザ動作を行うために、Helperのクラスが用意されています。
junit.extensions.jfcunit.JFCTestHelper
junit.extensions.jfcunit.RobotTestHelper
が、インスタンス可能な実装レベルのクラスになります。
これらのクラスは、ユーザの動作を行うメソッドが実装されています。
(メソッドの種類の詳細説明はJFCUnit付属のAPIドキュメントを参照して下さい。)
この2つのHelperの差は、クラス名称からもわかるように、RobotTestHelper の方は java.awt.Robot を使用した実装になっています。
JFCTestHelperは、AWTEvent にダイレクトにイベントを送る実装になっています。
見た目上では、JFCTestHelper は マウスカーソルは移動せず処理が進みますが、RobotTestHelper は マウスカーソルを移動しつつ
処理を進めるという差が分かります。
どちらがいいか?? ですが、両方の特性を知った上で判断して下さい。
語弊があるかもしれませんが、実際に手動で動作するのと全く同じにしたければ、RobotTestHelper の方を、
OSに依存しない形で、いつも同じ AWTEvent をCallしたい場合には JFCTestHelper を使う という判断になるかと思います。
イベントの種類と記述例は以下になります。
MouseClick(mousePressed + mouseReleased) → helper.enterClickAndLeave(new MouseEventData(this, comp,1));
Key入力 → helper.sendString(new StringEventData(this, comp, entryString));
***EventData というクラスを引数に渡します。JFCUnitのAPIを見てもらうと、いろんな種類のEventDataの実装があるのがわかると
思います。対象のコンポーネントにより、EventDataを使い分けるとより操作しやすくなります。
- Mouseイベント(AbstractMouseEventDataの実装)
JComboBoxMouseEventData, JListMouseEventData, JSpinnerMouseEventData, JTabbedPaneMouseEventData, JTableHeaderMouseEventData, JTableMouseEventData, JTextComponentMouseEventData, JTreeMouseEventData, MouseEventData - Keyイベント(AbstractKeyEventDataの実装)
KeyEventData, StringEventData - ドラッグイベント
DragEventData
注意点としては、 StringEventData(JFCTestCase _testCase, Component _comp, String _string) にて 入力文字列を渡せますが、
デフォルトでは日本語は対応していません。
StringEventData のソースを見てもらうと分かるのですが、文字を1桁ずつ対象のKeyStrokeを探し出すようになっており、そのMapping
情報は KeyMapの指定をしない限り、junit.extensions.jfcunit.keyboard.DefaultKeyMapping になります。(英数字、記号のみ)
●注意
Helperにてイベントを発生させる際、対象となるコンポーネントの参照を引数で渡します。つまり、対象のコンポーネントに
フォーカスを当ててから、指定のイベントを発生させるようになっています。
●補足
日本語ひらがなに対応したKeyMapを、JpnKeyMap.java :10KB にて提供しています。
helperに対して、setKeyMapping(new JpnKeyMap()); することにより反映します。
テストケース記述方法
では、実際のテストケースにてコンポーネントを操作し、チェックしてみましょう。
JFCUnitのサンプルの モーダルダイアログ(modal/EmployeeTestCase.java)をサンプルに見ていきましょう。
4行目、9行目、12行目 にて assert*** のメソッドによりチェックを行っています。
このチェックにてエラーの場合には、このテストケースは中断されます。つまり、4行目にてエラーが発生した
場合には、5行目以降のロジックは実行されません。
assertメソッドはJUnitと記述方法は同じになります。
assertメソッドは junit.framework.Assert に定義されています。
- 真であるか
assertTrue(String string, boolean boolean) - 偽であるか
assertFalse(String string, boolean boolean) - 等しいかどうか(.equals で判定)
assertEquals(String string, Object object, Object object2)
(比較対象は、Object,String,double,float,long,boolean,byte,char,short,int がそれぞれメソッドとして用意されています) - Nullでないかどうか
assertNotNull(String string, Object object) - Nullかどうか
assertNull(String string, Object object) - 同じかどうか(== で判定)
assertSame(String string, Object object, Object object2) - 同じでないかどうか
assertNotSame(String string, Object object, Object object2)
いづれの第一引数のStringはエラーの場合に出力される文字列になります。省略可能です。
また、fail*** のメソッドがありますが、これはテスト結果を否にするものになります。
このように、コンポーネントの動作、assertメソッドでのチェック・・・を繰り返しながら一連のテストを完成させます。
複数のテストケースを一括実行(Composite)
テストケースを記述したクラスをまとめて一括実行する場合には、Suiteクラスを使用します。
テストを追加する方法として、
と書いた場合、Test1_1クラス内のtest001という名称のメソッドのみがテストに追加されます。
このように本来は、テストの単位でaddしないといけないのですがそれを便利にしてくれる方法が
以下になります。
この場合、Test1_1のtestから始まるpublic/voidメソッドがテストに追加されます。
つまり、test***から始まるメソッドを一つ一つ追加する手間を省いてくれます。
ただし、テストが追加される順番は不定になります。
追加するメソッドは TestSuite内部にて java.lang.Class の getDeclaredMethods()
から取得できるメソッドの順番でaddしていくためです。
testメソッド名にて任意にソートをかけて追加してほしい場合は・・・自分でコーディング が必要です。参考:testをソートし追加するコードを一応載せておきます。
また、テストケースが大量になった場合は Tree構造で登録すると見やすくなります。
テスト実行(Builder)
テストの実行方法は、JUnitと同様に、コマンドベースでの実行、GUIベースでの実行が行えます。
コマンドベースはjunit.textui.TestRunner、GUIベースは junit.swingui.TestRunner にて実行します。
また、AntのReport機能を使用するとテスト結果がファイルレポートとして出力されます。
テストの拡張(Decorator)
テストとして、繰り返し○回行いたい・・・などのテストを行いたい場合のために、Decoratorが用意されています。
- TestSetUp
suiteオブジェクトの実行前にsetUpメソッド実行、実行後にtearDownメソッド実行します。
suite単位になるので、DBの接続やRMI接続などのような処理時に使用できます。 - RepeatedTest
同じテストを複数回繰り返して実行したい場合に使用します。
* 参考:testをソートし追加するコード
クラス内にある testメソッド を明示的にソートしてから追加するためのコードです。
この例では メソッド名を java.util.Collections#sort しています。
ご自由に書き換えOKですが・・・自己責任でお使いください(^-^;A