Fight the Future

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

LazyなDataProvider-TestNGをさらに深く理解してみる(3)

@ITさんの連載でデータ駆動テストで簡単にテストのパターンを増やすを解説した。
DataProviderはTestNGで注目を集める便利な機能だ。
DataProviderのメソッドはObject配列を戻り値とすればよいだけなので、
ファイルからでもデータベースからでもテストデータを取得できる。


たとえば、データベースからテストデータを取得するとこんなテストケースになるだろう。

public class NormalDataProividerTest {

	@DataProvider(name = "data")
	public Object[][] data() {
		DeptDao dao = new DeptDao();
		List<Dept> list = dao.listAllDept();

		Object[][] data = new Object[list.size()][];
		int index = 0;
		for (Iterator<Dept> iterator = list.iterator(); iterator.hasNext();) {
			Dept dept = (Dept) iterator.next();
			data[index] = new Object[] { dept };
			index++;
		}

		return data;
	}

	@Test(dataProvider = "data")
	public void test(Dept dept) {
		// call method with Model Object...
		Assert.assertTrue(true);
	}

}

DAOでデータベースからテストデータを取得して、テストメソッドの引数とする。

テストデータが大量にある場合問題となる

ただし、上記のサンプルはデータベースから取得するテストデータが大量である場合、問題を引き起こす。
それは、メモリの消費量だ。
データベースからデータを取得するということは、メモリ上に展開することになるわけで、
データが数百件程度ならまったく問題ないが、たとえば10万件テストデータがあるとOutOfMemoryになるかもしれない。

LazyなDataProvider

TestNGではそうした場合、Iteratorを使ったDataProviderを利用する。

public class LazyDataProviderTest {

	@DataProvider(name = "data")
	public Iterator data() {
		return new DeptIterator();
	}

	@Test(dataProvider = "data")
	public void test(Dept dept) {
		// call method with Model Object...
		Assert.assertTrue(true);
	}

	private static class DeptIterator implements Iterator {

		private int index;

		private DeptDao deptDao = new DeptDao();

		public boolean hasNext() {
			List<Dept> list = deptDao.listAllDept();
			if (index < list.size()) {
				return true;
			}
			return false;
		}

		public Object[] next() {
			List<Dept> list = deptDao.listAllDept();
			Object[] data = new Object[] { list.get(index) };
			index++;
			return data;
		}

		public void remove() {
			throw new UnsupportedOperationException();
		}
	}
}

DataProviderのメソッドが戻り値をObjectからIteratorに変えている。
TestNGはこのIteratorのhasNext()とnext()を呼び出し、テストメソッドに順次渡す。
サンプルではIteratorをstaticネストクラスにした。


メモリを消費しないようにできるトレードオフとしてhasNext()とnext()のそれぞれでデータベースにアクセスするためテストの実行に時間がかかる。
なお、サンプルではデータベースから全件取得しているから、Iteratorを使わないパターンとメモリの消費量としては変わらないけど、
たとえばhasNext()では件数だけSQLで取得するようにしてindexと比較すれば消費量は少なくなるとかできる。


テストデータを大量件数データベースから取得するときは、ぜひIteratorを使ったLazyなDataProviderを使ってください!