Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

プレゼン、ボランティアコーチします!

勉強会で話したいけど、プレゼンが初めて、苦手という方に無償でコーチします!

  • スライドのレビュー
  • 録画リハへのアドバイス

Twitter@jyukutyoまでメンションでもDMでも。 デブサミやJJUG CCCなど200人規模で登壇しました。海外での登壇も短いながらあり。デブサミ2017では公募スピーカー1位でした!

TestNGとJUnit4における、テストメソッドへのパラメータの考え方の違い

たとえば数値の引数を2つとって、それを足し算したりかけ算したりするメソッドをテストするとして。

  • public int add(int a, int b)
  • public int multiple(int a, int b)

みたいな。


こういうメソッドをテストする場合、引数のバリエーションを考えてテストする必要がある。
TestNGでもJUnit4でもこうしたバリエーションをパラメータとしてテストメソッドに渡す仕組みがあるが、個人的にはTestNGの方が進んでいると思う。


じゃあ実際にコードで違いを確認しよう。
さっきの足し算、かけ算するクラスと、それをJUnitでテストするクラスを作る。
足し算を3パターン、テストする。

import java.util.Arrays;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

class Sample {

	public int add(int a, int b) {
		return a + b;
	}

	public int multiple(int a, int b) {
		return a * b;
	}
}

@RunWith(Parameterized.class)
public class SampleTestByJUnit {

	private int a;

	private int b;

	private int expected;

	public SampleTestByJUnit(int a, int b, int expected) {
		this.a = a;
		this.b = b;
		this.expected = expected;
	}

	@Parameters
	public static List<Object[]> testData() {
		Object[][] datas =
			new Object[][] { { 1, 2, 3 }, { 0, 0, 0 }, { 4, 5, 9 } };
		return Arrays.asList(datas);
	}
	
	@Test
	public void testAdd() {
		Sample sample = new Sample();
		Assert.assertEquals(sample.add(a, b), expected);
	}

}

JUnit4でテストクラスにパラメータを渡すには。

  1. テストクラスに「@RunWith(Parameterized.class)」をつける
  2. 戻り値がコレクションでstaticなメソッドを作る
  3. ↑のメソッドに「@Parameters」をつける
  4. コンストラクタの引数でコレクションの要素を受け取る
  5. コンストラクタで引数の値をインスタンス変数に格納する
  6. テストメソッドでそのインスタンス変数を利用する

となる。今回、1つのテストで複数のパラメータを渡すために、コレクションの要素はObject配列にした。


JUnit4の考え方のキモは、あくまでテストメソッド自体は引数を取らないということだ。
だからコンストラクタでパラメータを受け取っている。


対して、TestNGではこうなる。

import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class SampleTestByTestNG {

	@DataProvider(name = "test-data")
	public Object[][] testData() {
		Object[][] datas =
			new Object[][] { { 1, 2, 3 }, { 0, 0, 0 }, { 4, 5, 9 } };
		return datas;
	}

	@Test(dataProvider = "test-data")
	public void testAdd(int a, int b, int expected) {
		Sample sample = new Sample();
		Assert.assertEquals(sample.add(a, b), expected);
	}
}
  1. 戻り値がObjectの2次元配列のメソッドを作る(staticである必要はない)
  2. ↑のメソッドに「@DataProvider」をつけ、name属性に任意の文字列を指定する。
  3. テストメソッドのdataProvider属性にDataProviderのnameを指定する
  4. テストメソッドの引数で配列の要素を受け取る

配列の要素はまたObject配列なので、Object配列の要素を1つずつ引数として受け取ってる。


TestNGの考え方のキモは、テストメソッド自体がパラメータを受け取るということだ。


どっちでもいいじゃん、というかもしれない。
けど、次かけ算するmultiple()メソッドのテストをするときはどう??


JUnitではパラメータのメソッドとテストメソッドに直接の関連がないため、
かけ算テストのパラメータを作ろうとしても、1つのパラメータメソッドを共有したり、コンストラクタを変更したりしないといけない。


対して、TestNGであれば、別のパラメータメソッドを作り(nameは別の文字列にして)、
かけ算のテストメソッドでそれを指定すればよい。


なので、この点はTestNGが進んでるな、と思う。