Fight the Future

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

TestNGとJUnit4のもっとも大きな違い

TestNGとJUnit4のもっとも大きな違いはテストクラスのインスタンス生成だ。


あるテストクラスに複数のテストメソッドがあるとき、2つのフレームワークはこう振る舞う。


なので、JUnit4の方が大量のテストケースを実行すると、(インスタンス生成の分)コストがかかる。
ただ、これは一概に悪とは言えなくて、各テストメソッドの独立性に深くかかわってる。

テストメソッドの独立性

複数のテストメソッドを実行するときに同じインスタンスを使い回すと、インスタンス変数の値によってテストの成否が変わる可能性がある(テストクラスにインスタンス変数がある場合など)。
つまりテストが独立していないということ。
JUnit4は(3の時代からだが)この考え方を忠実に守って、テストメソッドごとにインスタンスを生成してる。


一方TestNGはこの点も考慮した上で、テストクラスのインスタンスは1つしか作らない、という設計にしている。それは速さの点もあるし、状態を共有するのは自然なことで、それによってよいデザインのテストになるという点もある。

せっかくなので実証してみる

ソースで実証します。

テスト対象クラスはこれ。

package diff.junitortestng;

public class Target {

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

}

足し算するメソッドadd()だけがある単純なクラスです。


ではこれをテストするJUnit4のテストケースを作成します。

package diff.junitortestng;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class JUnitTest {

	public JUnitTest() {
		System.out.println("JUnitTest class is instanciated.");
	}

	@Test
	public void verifyAdd1() {
		Target t = new Target();
		assertEquals(t.add(3, 4), 7);
	}

	@Test
	public void verifyAdd2() {
		Target t = new Target();
		assertEquals(t.add(2, 1), 3);
	}

	@Test
	public void verifyAdd3() {
		Target t = new Target();
		assertEquals(t.add(5, 4), 9);
	}
}

コンストラクタでコンソールに出力します。テストメソッドは3つあります。
これを実行すると当然テストは成功で、以下のように出力します。

JUnitTest class is instanciated.
JUnitTest class is instanciated.
JUnitTest class is instanciated.

コンストラクタを3回呼び出してます。


同じことをするTestNGのテストケースを作成します。

package diff.junitortestng;

import static org.testng.Assert.assertEquals;

import org.testng.annotations.Test;

public class TestNGTest {

	public TestNGTest() {
		System.out.println("TestNGTest class is instanciated.");
	}

	@Test
	public void verifyAdd1() {
		Target t = new Target();
		assertEquals(t.add(3, 4), 7);
	}

	@Test
	public void verifyAdd2() {
		Target t = new Target();
		assertEquals(t.add(2, 1), 3);
	}

	@Test
	public void verifyAdd3() {
		Target t = new Target();
		assertEquals(t.add(5, 4), 9);
	}
}

同じアノテーション、assert()メソッドですが、すべてTestNGのものです(import文を見てください)。


このテストを実行すると、こんな出力です。

TestNGTest class is instanciated.
PASSED: verifyAdd2
PASSED: verifyAdd1
PASSED: verifyAdd3

===============================================
    diff.junitortestng.TestNGTest
    Tests run: 3, Failures: 0, Skips: 0
===============================================

コンストラクタを1回しか呼び出していないことがわかります。