Fight the Future

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

DbUnitライブラリ@TableAssertでDBとアサートできます

DbUnitNG プロジェクト日本語トップページ - SourceForge.JPで期待値のファイルとデータベースのテーブルをアサートするアノテーションを作りました。
@TableAssertです。


こんな感じ。

	@TableAssert(names = "dept", pathname = "insert_expected.xml")
	@SetUpOperation(pathname = "dept.xml", value = DatabaseOperationType.DELETE_ALL)
	public void testTableAssert() {
		insert();
	}

必須属性はnamesとpathnameです。
この2つだけなら、namesにデータベースのテーブル名を記述します。
つまり、今回はdeptテーブルと期待値ファイルをアサートします。


アサートは、期待値のファイルにあるカラムだけ比較します。
pathnameには期待値のファイルを記述します。この場合、「insert_expected.xml」が期待値のファイルです。
ファイル名だけであれば、テストクラスと同パッケージに配置します。
内容はDbUnitの形式です。


SQL文を直接記述することもできます。

	@TableAssert(names = "dept", pathname = "org/dbunitng/sample/dao/insert_expected.xml", queries = "select dname, deptno, loc from dept")
	@SetUpOperation(pathname = "dept.xml", value = DatabaseOperationType.DELETE_ALL)
	public void testTableAssert2() {
		insert();
	}

queries属性にはSQL文を記述します。その結果がDbUnitのITableとなります。ITableのテーブル名はnames属性に記述した名前になります。
つまり、この例ではSQL文の結果がITableとなり、テーブル名はdeptとなります。
XMLで表現すると以下のイメージです。

<dataset>
	<DEPT DEPTNO="1" DNAME="test" LOC="Osaka" />
</dataset>

これを実装するのに散々悩んだのですが、結局一番ダサい方法で実装しました。

ダウンキャスト。。。
ITestListener#onStart(ITestContext context)の引数ITestContextを実装クラスTestRunnerクラスにキャストしてます。


で、リスナーのリストにアクセスします。
リストを取得したら、DbUnitNGライブラリのリスナーが先頭に来るようにソートしてます。

		TestRunner runner = (TestRunner) context;
		List<ITestListener> list = runner.getTestListeners();

		Collections.sort(list, new Comparator<ITestListener>() {
			public int compare(ITestListener o1, ITestListener o2) {
				if (o1 instanceof DbUnitNGTestListener) {
					return -1;
				} else if (o2 instanceof DbUnitNGTestListener) {
					return 1;
				}
				return 0;
			}
		});

あーJavaなのにインタフェースの実装クラスを意識してダウンキャストなんて、一番ダサいプログラミングだよね。。。
他にいい方法が思いつかなかった。。。

TestNG eclipseプラグインでのリスナークラス登録の続き

TestNGプラグインのリスナーより順序を前で自分のリスナーを登録したい - Fight the Future じゅくのblogで困っているわけだけど、
こりゃTestNGのソースを変えないと無理かもしれないなあ。


TestNGeclipseプラグインはTestNGクラスではなくサブクラスのRemoteTestNGクラスを使ってリモート実行してる。
そのRemoteTestNGクラスのstaticネストクラス*1がこれ。

private static class DelegatingTestRunnerFactory implements ITestRunnerFactory {
    public TestRunner newTestRunner(ISuite suite, XmlTest test) {
      TestRunner tr= m_delagateFactory.newTestRunner(suite, test);
      tr.addListener(new RemoteMessageSenderTestListener(suite, test, m_messageSender));

      return tr;
    }
  }

TestRunner#addLisnter()で真っ先にRemoteMessageSenderTestListenerクラスをとうろくしちゃってる。
つまりリモートであるeclipseプラグインのUIが一番先に変わってしまう問題は解決できない。


んー、TestNGのリスナーに優先順位をつけれたらいいのになあ。

*1:インナークラスじゃない[http://d.hatena.ne.jp/jyukutyo/20061020/1161314906:title]

DbUnitNG0.3リリース!

DbUnitNG プロジェクト日本語トップページ - SourceForge.JP

  • 期待値ファイルからデータセットを作成する際に、ReplacementDataSetを使用するようにしました。
  • @SetupOperationにおいてテストスイート全体でデフォルトのでDatabaseOperationを設定できるようにしました。
  • データベースへの接続情報をアノテーションでも設定できるようにしました。

TestNG ML(英語)に投げてみた

TestNGプラグインのリスナーより順序を前で自分のリスナーを登録したい - Fight the Future じゅくのblogがどうしてもわからないので、
testng-users |
Google Groups
に投げてみた。

Hi,

With TestNG eclipse plugin, I add my listener class.
Then listener List that are returned by ITestResultNotifier#getTestListeners() are the order as follows.

[org.testng.remote.strprotocol.RemoteMessageSenderTestListener@50cf54, 
org.testng.reporters.TextReporter@5bc8c9, 
org.dbunitng.listeners.DbUnitNGTestListener@db3aac, 
org.testng.TestNG$ExitCodeListener@e90abf, 
org.testng.TestListenerAdapter@c0b3d1]

I'd like to add my listener class(DbUnitNGTestListner) before RemoteMessageSenderTestListener class.
What can I do?

Thanks, 

もちろん、英語のMLに送るのなんて初めて。
というか、オープンソースのMLに送るのなんて日本語でもやったことない。


たぶん英語めちゃくちゃ。
文章を作るのにあんまり時間かけすぎるのもどうかと思って。


Cedric、回答を返してくれるといいなあ。

TestNGプラグインのリスナーより順序を前で自分のリスナーを登録したい

eclipseTestNGプラグインを利用してデバッグ実行すると、
リスナーは以下の順で Listに入っていた。

[org.testng.remote.strprotocol.RemoteMessageSenderTestListener@50cf54, 
org.testng.reporters.TextReporter@5bc8c9, 
org.dbunitng.listeners.DbUnitNGTestListener@db3aac, 
org.testng.TestNG$ExitCodeListener@e90abf, 
org.testng.TestListenerAdapter@c0b3d1]

RemoteMessageSenderTestListenerがTestNGプラグインのリスナーであり、
このリスナーが先にきているため先ほどのようにeclipseのUIが先に変わってしまう。
TestNGのリスナーより先の順番に自分のリスナーを登録したい。
XMLで先頭に書こうがデフォルトリスナーとしてeclipseに登録しようが、
どうしても現状TestNGプラグインより前に登録できない><


どなたかリスナーの登録順序を制御する方法を知っていたら教えてください!

@TableAssertでアサートしたかったけど、失敗

誰か助けてください><


期待値とDBのテーブルを比較するようなテストなら、アサートもアノテーションでできるようにしようと考えた。
こんな感じで。

@TableAssert(names = "dept", pathname = "insert_expected.xml", queries = "select dname, deptno, loc from emp")
@SetUpOperation(pathname = "dept.xml", value = DatabaseOperationType.DELETE_ALL)
public void testTableAssert2() {
	insert();
}

XMLにある期待値とSELECT文の結果をアサートするイメージ。

いちおう、できたんだ。
コマンドラインから実行したり、main()メソッドで実行する分には。

eclipseプラグインで袋小路

でも、eclipseプラグインで実行するとダメ。
テーブルと期待値が違っていてアサートに失敗する場合でも、UIではPASSEDになっちゃう。
f:id:jyukutyo:20080822144506p:image
コンソールはこう出力される。

[DbUnitNGDatabaseOperation] [DEBUG] Database Operation is DELETE_ALL.
[DbUnitNGDatabaseOperation] [DEBUG] Database Operation is DELETE_ALL.
PASSED: testTableAssert
PASSED: testTableAssert2

===============================================
    sample
    Tests run: 2, Failures: 0, Skips: 0
===============================================

[DbUnitNGTestListener] [DEBUG] DbUnitNGTestListener#onFinish
Test Suite ends.
execute time : (865 ms)
===============================================
DbUnitNG
Total tests run: 2, Failures: 2, Skips: 0
===============================================

スイート全体ではFailureになるけど、途中では PASSEDのまま。


これ、TestNGのeclipseプラグインがTestNGのRemoteClinetでテストを起動してるからだと思う。
RemoteClinetだと、プラグインでUIに表示するリスナーにはオブジェクトじゃなく文字列で通知が行く。

public void onTestSuccess(TestResultMessage trm) {

一方、僕がアノテーションでテーブルとアサートしているのは普通のリスナー。

	public void onTestSuccess(ITestResult result) {
		
		TableAssert tableAssert = this.getAnnotation(result, TableAssert.class);
		if (tableAssert != null) {
			assertTable(tableAssert, result);
		}
		onTestFinishWhateverHappens(result);
	}

	protected void assertTable(TableAssert tableAssert, ITestResult result) {

		IDataSet actual = createQueryDataSet(tableAssert, result);
		IDataSet expected = toDataSet(tableAssert.pathname(), result);
		try {
			Assertion.assertEquals(expected, actual);
		} catch (Throwable e) {
			result.setStatus(ITestResult.FAILURE);
			result.setThrowable(e);
		}

	}

アサートのところで、テストのステータスをFAILUREに上書きしてるけど、RemoteClinetにはすでに文字列で通知が行ってるから、変更は反映されない。


だからプラグインのUIではPASSED、コンソールにはFAILUREと表示されてしまう。。。
これはさすがに使えないから、アノテーションでのアサートはあきらめないといけないかな。。。


eclipseプラグインやTestNGに詳しい人、もしアイデアをお持ちでしたらぜひ教えてください!

DbUnitで読み込むファイルの値にnullを設定する

DbUnitでは初期値や期待値をXMLやExcelに記述することができる。
ただ、nullを表現したいときは困ってしまう。
たとえばXMLで次のように記述したとする。

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
  <HUMAN NAME="jyukutyo" NULLCOLUMN="" />
</dataset>

この場合、NULLCOLUMNはnullではなく空文字となる。
本当にそうなるかサンプルプログラムで読み込んで出力してみる。

	public static void main(String[] args) throws Exception {

		FlatXmlDataSet dataSet = new FlatXmlDataSet(new File("data.xml"));
		output(dataSet);

	}

	protected static void output(IDataSet dataSet) throws DataSetException {
		for (String name : dataSet.getTableNames()) {
			ITable table = dataSet.getTable(name);
			System.out.println("Table name:"
				+ table.getTableMetaData().getTableName());
			Column[] columns = table.getTableMetaData().getColumns();
			for (int i = 0; i < table.getRowCount(); i++) {
				System.out.print("Row:" + i + "[");
				for (int j = 0; j < columns.length; j++) {
					Column column = columns[j];
					System.out.print(column.getColumnName() + "={"
						+ table.getValue(i, column.getColumnName()) + "},");
				}
				System.out.println("]");
			}
		}
	}

さっきのXMLをdata.xmlとしてる。
実行するとこう出力される。

Table name:HUMAN
Row:0[NAME={jyukutyo},NULLCOLUMN={},]

もし、nullであればこう出力されなければならない*1

Table name:HUMAN
Row:0[NAME={jyukutyo},NULLCOLUMN={null},]

Excelを使っていても同様の事態になる。


ではDbUnitはnullを初期値や期待値として表現できないのか、
というとそうではなくて、ReplacementDataSetクラスを利用する。
これはDataSetをラップするオブジェクトで、後から値を置き換えることができる。


なので、XMLをこう変更する。

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
	<HUMAN NAME="jyukutyo" NULLCOLUMN="[null]" />
</dataset>

[null]と書いたがこれは任意でよく、後から置き換えるためのマーカーにすぎない。
プログラムで[null]をnullに置き換えるよう変更する。

	public static void main(String[] args) throws Exception {

		FlatXmlDataSet dataSet = new FlatXmlDataSet(new File("data.xml"));
		
		ReplacementDataSet replacementDataSet = new ReplacementDataSet(dataSet);
		replacementDataSet.addReplacementObject("[null]", null);
		
		output(replacementDataSet);

	}

実行するとNULLCOLUMNはnullになる。

Table name:HUMAN
Row:0[NAME={jyukutyo},NULLCOLUMN={null},]

これを応用すれば、たとえばTimestampで現在時刻を設定するということもできる。

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
	<HUMAN NAME="jyukutyo" NULLCOLUMN="[null]" TIMESTAMP="[now]" />
</dataset>
replacementDataSet.addReplacementObject("[now]", new Timestamp(System.currentTimeMillis()));
Table name:HUMAN
Row:0[NAME={jyukutyo},NULLCOLUMN={null},TIMESTAMP={2008-08-21 13:52:22.044},]

ちなみに、置き換える値が複数の行にあっても、すべて置換される。

*1:+演算子はStringBuffer#append()だから

DbUnitでSQL文の結果をそのままデータセットにする

	public static void main(String[] args) throws Exception {

		JdbcDatabaseTester tester =
			new JdbcDatabaseTester(
				"com.mysql.jdbc.Driver",
				"jdbc:mysql://127.0.0.1/testframework",
				"root",
				"root");
		
		QueryDataSet dataSet = new QueryDataSet(tester.getConnection());
		// retrieve all rows from specified table
		dataSet.addTable("EMP");
		
		// retrieve rows that is result of argument query
		dataSet.addTable("EMP_UNDER_7500", "select * from emp where empno < 7500");

		output(dataSet);
	}

	protected static void output(IDataSet dataSet) throws DataSetException {
		for (String name : dataSet.getTableNames()) {
			ITable table = dataSet.getTable(name);
			System.out.print("Table name:"
				+ table.getTableMetaData().getTableName());
			System.out.println("\tRow count:" + table.getRowCount());
		}
	}

QueryDataSet#addTable()でデータベースにあるレコードをデータセットに追加します。
引数が1つのものは、DBのテーブル名を渡します。そのテーブルのレコードをすべてITableにしてデータセットに追加します。
引数が2つのものは、第1引数は任意の文字列です。それがITableの名前になります。第2引数に実行するSQLを記述します。


このサンプルを実行するとこんな感じです。

Table name:EMP	Row count:14
Table name:EMP_UNDER_7500	Row count:2

TestNGでXMLを使わずにテストを実行する

TestNGではtestng.xml(ファイル名は任意)というXMLを使ってテストを実行するのですが、
XMLを使わずにプログラミングでテストを実行することもできます。

	public static void main(String[] args) {
		TestNG testNG = new TestNG();

		testNG.addListener(new DbUnitNGTestListener());
		testNG.addListener(new PerformanceListener());
		TestListenerAdapter adapter = new TestListenerAdapter();
		testNG.addListener(adapter);

		testNG.setTestClasses(new Class[] { PerformanceTest.class });

		testNG.run();

		System.out.println("PassedTests:" + adapter.getPassedTests());
		System.out.println("FailedTests:" + adapter.getFailedTests());
		System.out.println("SkippedTests:" + adapter.getSkippedTests());

	}

TestNGオブジェクトを生成して、run()メソッドを呼ぶことでテストを実行します。
リスナークラスを追加することもできます。
ただ、普通にrun()を呼び出しただけでは、テスト成功と失敗が数でしかわかりません。
こんな感じで。

===============================================
Command line suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================

なので、詳細に出力するためにはTestListenerAdapterオブジェクトをリスナーとして登録します。
TestListenerAdapterはTestNGが提供するクラスで、テスト結果に関するさまざまな情報を取り出すことができます。
このサンプルでは成功、失敗、スキップしたテストをコンソールに出力してます。

PassedTests:[[TestResult: testPerformance STATUS:1 METHOD:org.dbunitng.sample.PerformanceTest.testPerformance()]]
FailedTests:[]
SkippedTests:[]

XMLの設定をプログラミングする

ですが、TestNGクラスには、XMLで記述できたパラメータなどを設定するメソッドはありません。

<parameter name="driver" value="com.mysql.jdbc.Driver"></parameter>
<parameter name="url" value="jdbc:mysql://127.0.0.1/testframework"></parameter>
<parameter name="username" value="root"></parameter>
<parameter name="password" value="root"></parameter>

パラメータをプログラミングで設定するためには、XmlSuiteオブジェクトを利用します。

	public static void main(String[] args) {
		TestNG testNG = new TestNG();

		testNG.addListener(new DbUnitNGTestListener());
		TestListenerAdapter adapter = new TestListenerAdapter();
		testNG.addListener(adapter);

		testNG.setTestClasses(new Class[] { DbUnitNGAnnotationTest.class });

		XmlSuite suite = new XmlSuite();
		Map<String, String> parameters = new HashMap<String, String>();
		parameters.put("driver", "com.mysql.jdbc.Driver");
		parameters.put("url", "jdbc:mysql://127.0.0.1/testframework");
		parameters.put("username", "root");
		parameters.put("password", "root");
		parameters.put("defaultOperation", "CLEAN_INSERT");
		suite.setParameters(parameters);

		testNG.setXmlSuites(Arrays.asList(new XmlSuite[] { suite }));

		testNG.run();

		System.out.println("PassedTests:" + adapter.getPassedTests());
		System.out.println("FailedTests:" + adapter.getFailedTests());
		System.out.println("SkippedTests:" + adapter.getSkippedTests());

	}

XmlSuiteオブジェクトを生成し、Mapでパラメータを表します。
MapをXmlSuiteにセットし、TestNGオブジェクトにXmlSuiteをセットします。
ただ、これでは例外が発生します。

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.AbstractList.add(AbstractList.java:151)
	at java.util.AbstractList.add(AbstractList.java:89)
	at org.testng.TestNG.initializeCommandLineSuites(TestNG.java:630)
	at org.testng.TestNG.run(TestNG.java:690)

どうやらArrays.asList()で生成したリストに対してTestNGが内部で要素を追加しようとしたようです*1
なので、こうしました。

	public static void main(String[] args) {
		TestNG testNG = new TestNG();

		testNG.addListener(new DbUnitNGTestListener());
		TestListenerAdapter adapter = new TestListenerAdapter();
		testNG.addListener(adapter);

		testNG.setTestClasses(new Class[] { DbUnitNGAnnotationTest.class });

		XmlSuite suite = new XmlSuite();
		Map<String, String> parameters = new HashMap<String, String>();
		parameters.put("driver", "com.mysql.jdbc.Driver");
		parameters.put("url", "jdbc:mysql://127.0.0.1/testframework");
		parameters.put("username", "root");
		parameters.put("password", "root");
		parameters.put("defaultOperation", "CLEAN_INSERT");
		suite.setParameters(parameters);

		List<XmlSuite> list = new ArrayList<XmlSuite>();
		list.add(suite);
		testNG.setXmlSuites(list);

		testNG.run();
// the rest is ommitted...

しかし、それでも例外が発生します。

Exception in thread "main" org.dbunitng.exception.DbUnitNGRuntimeException: Required Parameters are not specified in TestNG Suite.
org.dbunitng.listeners.internal.DbUnitNGConfig@1dddba[
  driver=<null>
  url=<null>
  userName=<null>
  password=<null>
  isDbcp=false
  schema=<null>
  factory=<null>
  defaultOperation=NONE
]

Mapで設定したパラメータが取得できないようです。
テストでパラメータを利用したい場合、TestNG#setTestClasses()でテストクラスをセットするのではなく、XmlTest#setXmlTestClasses()を使う必要があるようです。


なので、最終的にパラメータを設定したテストは次のようになります。

	public static void main(String[] args) {
		TestNG testNG = new TestNG();

		testNG.addListener(new DbUnitNGTestListener());
		TestListenerAdapter adapter = new TestListenerAdapter();
		testNG.addListener(adapter);

		XmlSuite suite = new XmlSuite();
		Map<String, String> parameters = new HashMap<String, String>();
		parameters.put("driver", "com.mysql.jdbc.Driver");
		parameters.put("url", "jdbc:mysql://127.0.0.1/testframework");
		parameters.put("username", "root");
		parameters.put("password", "root");
		parameters.put("defaultOperation", "CLEAN_INSERT");
		suite.setParameters(parameters);

		XmlTest test = new XmlTest(suite);
		XmlClass[] classes =
			new XmlClass[] { new XmlClass(DbUnitNGAnnotationTest.class) };
		test.setXmlClasses(Arrays.asList(classes));

		List<XmlSuite> list = new ArrayList<XmlSuite>();
		list.add(suite);
		testNG.setXmlSuites(list);

		testNG.run();
// the rest is ommitted...

これでセットしたパラメータを用いてテストを実行できます。

*1:Arrays.asList()が生成するリストは不変リスト

DbUnitライブラリでデフォルトのDatabaseOperationを指定できます

@SetupOperationでDbUnitのDatabaseOperationを指定できるわけですが、
テストメソッドごとに毎回指定するのは手間なので、
テストスイート全体でデフォルトを設定できるようにしました。


接続情報と同様に、
testng.xmlか@DbUnitNGアノテーションで指定することができます。
もちろんデフォルトを指定しないこともできます。

こんな感じです。

@BeforeSuite
@DbUnitNG(driver = "com.mysql.jdbc.Driver", password = "root", url = "jdbc:mysql://127.0.0.1/testframework", username = "root", defaultOperation = DatabaseOperationType.CLEAN_INSERT)
public void beforeSuite() {}

アノテーションであればdefaultOperation属性にEnumのDatabaseOperationTypeを指定します。
XMLであれば要素にname「defaultOperation」で指定します。

<suite name="DbUnitNG">
	<parameter name="defaultOperation" value="CLEAN_INSERT"></parameter>
	<parameter name="driver" value="com.mysql.jdbc.Driver"></parameter>
	<parameter name="url" value="jdbc:mysql://127.0.0.1/testframework"></parameter>
	<parameter name="username" value="root"></parameter>
	<parameter name="password" value="root"></parameter>

defaultOperationの値は次の文字列を指定します。

  1. NONE
  2. UPDATE
  3. INSERT
  4. REFRESH
  5. DELETE
  6. DELETE_ALL
  7. TRUNCATE_TABLE
  8. CLEAN_INSERT


もちろん、デフォルトを指定していても@SetUpOperationや@TearDownOpearionで指定すれば、後者を優先します。

DbUnitライブラリで接続情報をアノテーションにしました

DbUnitNG プロジェクト日本語トップページ - SourceForge.JPで、データベースへの接続情報をアノテーションでも設定できるようにしました。
と同時に、スキーマも記述できるようにしました。
さらに、JDBCのURLから判断して、DbUnitのIDataTypeFactoryを自動的に設定するようにしました(後述)。

ライブラリDbUnitNGでは接続情報を設定する方法が2つあります。

  1. testng.xml
  2. アノテーション

XMLだとこんな感じです。

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="DbUnitNG">
	<parameter name="driver" value="com.mysql.jdbc.Driver"></parameter>
	<parameter name="url" value="jdbc:mysql://127.0.0.1/testframework"></parameter>
	<parameter name="username" value="root"></parameter>
	<parameter name="password" value="root"></parameter>
	<parameter name="schema" value="testframework"></parameter>
	<listeners>
		<listener class-name="org.dbunitng.listeners.DbUnitNGTestListener"></listener>
	</listeners>
	<test verbose="2" name="sample" annotations="JDK">
		<classes>
			<class name="org.dbunitng.sample.PerformanceTest">
			</class>
		</classes>
	</test>
</suite>

同じ設定をアノテーションでするとこうなります。

@BeforeSuite
@DbUnitNG(driver = "com.mysql.jdbc.Driver", password = "root", url = "jdbc:mysql://127.0.0.1/testframework", username = "root", schema = "testframework")
public void beforeSuite() {}

接続情報を記述するアノテーションは@DbUnitNGです。
XMLの記述内容と同じですが、必ず@BeforeSuiteと併用しないといけません。
@BeforeSuite + @DbUnitNGで読み取れます。


もし、XMLアノテーション両方とも使った場合、アノテーションを優先します。

IDataTypeFactory

DbUnitのIDataTypeFactoryは各データベース独自のデータ型をサポートするためのものです。
たとえば、Oracleを使っている場合、OracleDataTypeFactoryオブジェクトをDbUnitのDatabaseConfigオブジェクトにセットします。

config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,factory);

ライブラリDbUnitNGではこの設定を自動的にやります。
JDBCのURLに含まれるデータベース名から判断してます。

jdbc:sqlserver://hostname:9999 MsSqlDataTypeFactory
jdbc:oracle:thin:@hostname:1521:orcl OracleDataTypeFactory
jdbc:db2://host:9999/database Db2DataTypeFactory
jdbc:mysql://127.0.0.1/testframework MySqlDataTypeFactory
jdbc:hsqldb:hsql://127.0.0.1:9001 HsqldbDataTypeFactory
jdbc:h2:tcp://localhost:9092/MyDB H2DataTypeFactory

これら以外のデータベースならデフォルトのファクトリを使います。

DbUnitNG0.2リリース!

テストケースの追加と、バグを修正しました!
DbUnitNG プロジェクト日本語トップページ - SourceForge.JP
AssertionHelperクラスを使えばラクにアサートすることができます。

List<Dept> deptList = deptDao.listAllDept();
AssertionHelper.assertEqualsOnlyColumnsInFile(
	deptList,
	getClass(),
	"dept.xml");

単に内部ではリストをデータセットに変換して、ファイルと比較してるだけなんですけどね。
getClass()してるのは、ファイルをテストクラスと同じパッケージから探すためです。
テストクラスが「aaa.bbb.ccc.HogeTest」であれば、「aaa/bbb/ccc/dept.xml」を読み込みます。


メソッド名のとおり、ファイルに記述したカラムのみ比較します。
たとえば、BeanであるDeptクラスにこういうプロパティがあるとします。

  • dname
  • deptno
  • location
  • updateDate

updateDateは更新日付と考えましょう。
一方、XMLはこういう記述です。

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
	<DEPT DEPTNO="10" DNAME="ACCOUNTING" LOCATION="NEW YORK" />
	<DEPT DEPTNO="20" DNAME="RESEARCH" LOCATION="DALLAS" />
	<DEPT DEPTNO="30" DNAME="SALES" LOCATION="CHICAGO" />
	<DEPT DEPTNO="40" DNAME="OPERATIONS" LOCATION="BOSTON" />
</dataset>

updateDateはありません。
なので、アサートではdname,deptno,locationのみ比較します。


DbUnitのデフォルトでは、こういうカラムが異なる場合、アサートが失敗してしまいます。
自分でDefaultColumnFilter.includedColumnsTable()とかexcludedColumnsTable()とかしないといけない。
なのでAssertionHelperに組み込んでおきました。
もちろん通常のDbUnitのアサートをしたいときは、普通にDbUnitのAssertionクラスを利用すればいいわけです。