2012/07/27

JUnit3 で Parameterized test case (1)

JUnit4 には Parameters アノテーションがあって、同じテストをパラメータを変えて実行することができます。
しかし、JUnit3 にはそのような機能がないため、本来はパラメータ毎にテストメソッドを記述しなければなりません。
なんてイマイチな仕様なんだ…

しかし、よく考えてみると、 TestSuite を利用すれば、JUnit4 と同じように Parameterized test が出来ることに気づきました。
以下、やってみた結果です。

今回、作成したプロジェクトは以下においてあります。
ご自由にお使いください。
JUnit3Experiment_01.zip

2012/7/28 追記
ちょっとだけ不満が残ったので、改造してみました。
穀風: JUnit3 で Parameterized test case (2)

テスト対象を準備

まずは、テスト対象のプログラムを作成します。
今回は、入力値の2倍を返すメソッド、入力値の自乗を返すメソッドの2つを用意してみました。 あえて、バグを仕込んであります。

MyUtils.java
public class MyUtils {
    public static int calcTwice(int a) {
        if (a < 100) {
            return a * 2;
        } else {
            // 100 以上の場合は間違える Bug
            return a;
        }
    }

    public static int calcSquare(int a) {
        if (a < 100) {
            return a * a;
        } else {
            // 100 以上の場合は間違える Bug
            return a;
        }
    }
}

TestCase を作成

テストを書きます。
calcTwice(int) の TestCase は以下です。

CalcTwiceTest.java
public class CalcTwiceTest extends TestCase {
    private final int mExpected;
    private final int mInput;

    CalcTwiceTest(int number, int expected, int input) {
        // テスト名がユニークになるようにする
        super("TwiceTest " + number);

        mExpected = expected;
        mInput = input;
    }

    @Override
    protected void runTest() throws Throwable {
        int result = MyUtils.calcTwice(mInput);
        assertEquals("input " + mInput, mExpected, result);
    }
}

このとき、コンストラクタにパラメータを取るようにします。
後ほど説明しますが、パラメータの数だけ TestCase インスタンスが作成されることになります。
そのため、親コンストラクタに名前を明示的に指定して、重複しないようにしなければなりません。
「クラス名+[数字or特徴的なパラメータ]」にするのが良いでしょう。

また、runTest() をオーバーライドして、テストを記述します。
JUnit に慣れている方は、「おや?」と思ったかもしれません。
普通、JUnit では、testXXX() がテストメソッドとして自動的に使用されますので、runTest() を明示的にオーバーライドすることはありません。
しかし、今回のようにコンストラクタを独自形式にした場合、この機能が働きませんので、このような形式にする必要があるのです。

同様に、calcSquare(int)TestCase は以下のようになります。

CalcSquareTest.java
public class CalcSquareTest extends TestCase {
    private final int mExpected;
    private final int mInput;

    CalcSquareTest(int number, int expected, int input) {
        // テスト名がユニークになるようにする
        super("SquareTest " + number);

        mExpected = expected;
        mInput = input;
    }

    @Override
    protected void runTest() throws Throwable {
        int result = MyUtils.calcSquare(mInput);
        assertEquals("input " + mInput, mExpected, result);
    }
}


TestSuite の作成

TestSuite とは、複数の TestCase をまとめたものです。
今回は、各パラメータ毎に TestCase のインスタンスを作成し、それらをまとめて TestSuite とします。

AllTests.java
public class AllTests {
    // Test Data
    // 本当はFile等から取り込む
    private static int[][] sTwiceTestData = {
            {5, 10},
            {8, 16},
            {20, 40},
            {21, 42},
            {103, 206}
    };

    // Test Data
    // 本当はFile等から取り込む
    private static int[][] sSquareTestData = {
        {5, 25},
        {8, 64},
        {20, 400},
        {21, 441},
        {103, 10609}
    };

    public static Test suite() {
        TestSuite suite = new TestSuite(AllTests.class.getName());
        //$JUnit-BEGIN$
        for (int i = 0; i < sTwiceTestData.length; i++) {
            suite.addTest(new CalcTwiceTest(i, sTwiceTestData[i][1], sTwiceTestData[i][0]));
        }
        for (int i = 0; i < sSquareTestData.length; i++) {
            suite.addTest(new CalcSquareTest(i, sSquareTestData[i][1], sSquareTestData[i][0]));
        }
        //$JUnit-END$
        return suite;
    }
}

Test の実行

Eclipse の場合、以下のように TestSuite を実行します。

AllTests.java を選択
Run As → JUnit Test

Eclipse JUnit Launcher を選択。

以下のような結果になりました。
ちゃんと、パラメータ毎に動作しています。バグもバッチリひろっていますね。

この方法、JUnit3 の思想的にどうなのか微妙なところですが、形式的にはJUnit4に近いですし、TestCase クラスの JavaDoc には、

Override to run the test and assert its state.

と書いてるので、上記のようなテストケースを作成するのは想定内のように思います。

0 件のコメント: