メモの日々


2016年06月06日(月) [長年日記]

[java] JUnitでparameterized testなどを使うサンプル

6月からJavaを使っている。JUnitのパラメータ化テストなどの機能を使ったので忘れないようにメモ。

を使った。パラメータ化テストは以前にTheoriesを使ったことがあるのだけれど、今回はParameterized testsを使ってみた。

次のサンプルでは、テストメソッドは testAdd(), testAdd2(), testDivide() の3つだけだけれどパラメータ化により15件のテストが実行される。testDivide()の実装はいまいち。

// テスト対象のクラス
public class Calculator {
    public static int add(int x, int y) {
        return x + y;
    }

    public static int divide(int x, int y) {
        return x / y;
    }
}
import java.util.Arrays;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import static org.hamcrest.CoreMatchers.is;
import org.junit.Rule;
import org.junit.experimental.runners.Enclosed;
import org.junit.rules.ExpectedException;
import org.junit.runners.Parameterized.Parameter;

// Calculatorのadd()とdivide()のテストを束ねるクラス。
@RunWith(Enclosed.class)
public class CalculatorTest {
    // Calculator::add のテスト
    @RunWith(Parameterized.class)
    public static class AddTest {
        // AddTest用のテストパラメータ
        @Parameters(name = "{index}: {0}")
        public static Iterable<P> data() {
            return Arrays.asList(
                    new P(1, 1, 2),
                    new P(1, 2, 3),
                    new P(0, 1, 1),
                    new P(-1, 1, 10), // わざとテストを失敗させる
                    new P(-1, 1, 0)
            );
        }

        // AddTest用テストパラメータを保持するクラス
        public static class P {
            int x;
            int y;
            int expected;

            P(int x, int y, int expected) {
                this.x = x;
                this.y = y;
                this.expected = expected;
            }

            @Override
            public String toString() {
                return String.format("x=%d, y=%d", x, y);
            }
        }

        // テストパラメータはメンバ変数に保持することになる
        @Parameter
        public P p;

        // これがテスト本体
        @Test
        public void testAdd() {
            assertThat(Calculator.add(p.x, p.y), is(p.expected));
        }

        @Test
        public void testAdd2() {
            assertThat(Calculator.add(p.y, p.x), is(p.expected));
        }
    }

    // Calculator::divide のテスト
    @RunWith(Parameterized.class)
    public static class DivideTest {
        @Parameters(name = "{index}: {0}")
        public static Iterable<P> data() {
            return Arrays.asList(
                    new P(1, 1, 1),
                    new P(1, 2, 0),
                    new P(0, 1, 0),
                    new P(-1, 0, null), // 例外が投げられるケース
                    new P(-1, 1, null)  // わざとテストを失敗させる
            );
        }

        public static class P {
            int x;
            int y;
            Integer expected;

            P(int x, int y, Integer expected) {
                this.x = x;
                this.y = y;
                this.expected = expected;
            }

            @Override
            public String toString() {
                return String.format("x=%d, y=%d", x, y);
            }
        }

        @Parameter
        public P p;

        // 例外を捕捉するのにExpectedExceptionルールを使う
        @Rule
        public ExpectedException thrown = ExpectedException.none();

        @Test
        public void testDivide() {
            // expectedがnullなら例外を捕捉することにする
            if (p.expected == null) thrown.expect(ArithmeticException.class);

            int result = Calculator.divide(p.x, p.y);

            if (p.expected != null) assertThat(result, is(p.expected));
        }
    }
}
Testsuite: CalculatorTest
Tests run: 15, Failures: 3, Errors: 0, Skipped: 0, Time elapsed: 0.047 sec

Testcase: testDivide[4: x=-1, y=1](CalculatorTest$DivideTest):	FAILED
Expected test to throw an instance of java.lang.ArithmeticException
junit.framework.AssertionFailedError: Expected test to throw an instance of java.lang.ArithmeticException


Testcase: testAdd[3: x=-1, y=1](CalculatorTest$AddTest):	FAILED

Expected: is <10>
     but: was <0>
junit.framework.AssertionFailedError: 
Expected: is <10>
     but: was <0>
	at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
	at CalculatorTest$AddTest.testAdd(CalculatorTest.java:56)


Testcase: testAdd2[3: x=-1, y=1](CalculatorTest$AddTest):	FAILED

Expected: is <10>
     but: was <0>
junit.framework.AssertionFailedError: 
Expected: is <10>
     but: was <0>
	at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
	at CalculatorTest$AddTest.testAdd2(CalculatorTest.java:61)