@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を使ってください!