GUIをテストするには?
しかし、AWTやSwingのようなプログラムの場合、ダミーにActionEventインスタンスを作ってメソッドを呼び出す――というやり方では、なんとなく納得できないところがありますね。「実際にボタンを押して動かすようなテストはできないのか?」と思う人も多いでしょう。
こういうテストを行なわせたい場合には、java.awt.Robotクラスを利用するとよいでしょう。これはAWTやSwingなどのGUI利用プログラムを自動制御するためのものです。これを利用して、ボタンをクリックさせるテスト・メソッドを考えてみましょう。
package jp.tuyano;
import static org.junit.Assert.*;
import org.junit.*;
import java.awt.*;
import java.awt.event.*;
import java.math.BigInteger;
import java.util.Random;
public class SampleAppTest {
SampleApp sampleapp;
Robot robot;
@Before
public void setUp() throws Exception {
sampleapp = new SampleApp();
sampleapp.setVisible(true);
robot = new Robot();
}
@Test
public void test() {
try {
Random random = new Random();
BigInteger BI_100 = new BigInteger("100");
BigInteger BI_105 = new BigInteger("105");
robot.setAutoDelay(100);
for(int i =0;i < 10;i++){
int n = random.nextInt(Integer.MAX_VALUE);
sampleapp.field.setText(Integer.toString(n));
robot.mouseMove(sampleapp.button.getX() + 10,
sampleapp.button.getY() + sampleapp.getInsets().top + 5);
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
BigInteger n0 = new BigInteger(sampleapp.field.getText());
BigInteger n1 = new BigInteger(sampleapp.field1.getText());
BigInteger n2 = new BigInteger(sampleapp.field2.getText());
BigInteger n3 = new BigInteger(sampleapp.field3.getText());
BigInteger res1 = n0.multiply(BI_100).divide(BI_105);
BigInteger res2 = n0.multiply(BI_105).divide(BI_100);
Assert.assertEquals(res1.toString(),n1.toString());
Assert.assertEquals(res2.toString(),n3.toString());
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
}
今回は、Robotの他にいろいろと追加をしてみました。ここでは、乱数を使ってintの最大値までの乱数を設定し、Robotでボタンをクリックさせてテストを行なわせています。また1回ではすぐ終わるので、10回繰り返して実行させるようにしてみました。
Robotによる操作は、意外に簡単です。Robotインスタンスを作成し、mouseMove、mousePress、mouseReleaseといったメソッドを呼び出すことでマウスポインタの位置やマウスボタンの状態(押し下げている、離している)を設定できます。乱数をfieldに設定し、buttonをRobotでクリックしてやればいいわけです。
それよりも問題は、「実行結果が正しいかどうか」をチェックする部分でしょう。JUnit 4には「Assert」というクラスが用意されており、さまざまな値のチェックを行なえます。ここでは「assertEquals」というものを使っていますが、これは2つのオブジェクトの値が等しいものかどうかどうかをチェックするものです。ここでは、ボタンをクリックして各フィールドに書き出された値と、直接計算して得られた値が同じかどうかをチェックすることで正しく処理が行なわれたかを調べよう、というわけです。
問題は、in値の最大値までをランダムに設定しているので、場合によってはintでは収まりきれない場合がある、という点です。まぁ、longを使えばいいのですが、今回は「どんな大きな値でも扱える」ということで、java.math.BigIntegerクラスを使ってフィールドの数値を取り出し、計算させることにしました。
実際に何度かテストを行なってみると、うまくいくこともありますが、途中でエラーが起こり、障害トレースに結果が表示されることもあります。いろいろ試してみると、やはりintの最大値(2,123,483,647)の近くになったあたりで値がおかしくなっていることがわかります。やはり、intの上限近くになって値がオーバーフローしているようですね。
今回は、GUIにかなり直結したprogramの作りになっているので少々わかりにくいサンプルになってしまいました。実際に利用する場合には、このように細かな操作を追跡していくよりも「オブジェクト」単位で状態をチェックするという使い方のほうが一般的でしょう。――ある程度の規模になると、処理やデータ類は専用のクラスとして定義し、インスタンスを作成して処理していくケースが増えてきます。こうした場合、インスタンス単位でassertEqualsを使ってチェックするような形でテストを用意しておけば、効率よくオブジェクトの状態を調べていけるわけです。
Eclipseには、この他に複数のテストクラスをまとめて実行するための機能なども用意することができます。必要に応じていくつかのテストクラスを定義しておけば、プログラムのさまざまな状況を必要に応じてテストできますね。――基本的な作業の流れがわかれば、単体テストはそう難しいものではありません。せっかくEclipseを使っているのですから、ぜひJUnitでテストを活用しましょう。