DBUnitを使うと簡潔にユニットテスト用のDBを用意できました

ネットの検索結果でチラチラとは見ていたのですが、この度初めて自分でもDBUnitを使ってみました。[ネット上のサンプルコード](https://github.com/marcphilipp/dbunit-examples)をほとんどそのまま実行しただけですが、私の手元でも簡単にユニットテスト用のDBとデータを準備することができました。

従来、[H2 Database](http://www.h2database.com/html/main.html)はRunScriptで初期化用のスキーマを流し込んでいたのですが、これならJDBCドライバを選ばない気がします。

※ 他のJDBCドライバでH2 DatabaseのINIT=RunScript相当の機能が可能なのか知りません。

##環境

* DBUnit 2.5.1
* JUnit 4.12

## ユニットテストのコード（パクリですが）

以下のような感じです。Excelファイルもデータソースとして使える点は、仕事でプログラムを作る人にとってかなりのアドバンテージな気がします。

```java:PersonRepositoryTest.java
import java.io.File;

import org.junit.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.h2.engine.Constants.UTF8;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;

import javax.sql.DataSource;

import org.dbunit.IDatabaseTester;
import org.dbunit.JdbcDatabaseTester;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.excel.XlsDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.operation.DatabaseOperation;
import org.h2.jdbcx.JdbcDataSource;
import org.h2.tools.RunScript;

public class PersonRepositoryTest {

	private static final String JDBC_DRIVER = org.h2.Driver.class.getName();
	private static final String JDBC_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";
	private static final String USER = "sa";
	private static final String PASSWORD = "";

	@BeforeClass
	public static void createSchema() throws Exception {
		RunScript.execute(JDBC_URL, USER, PASSWORD, "schema.sql", UTF8, false);
	}

	@Before
	public void importDataSet() throws Exception {
		IDataSet dataSet = readDataSet();
		cleanlyInsert(dataSet);
	}

	private IDataSet readDataSet() throws Exception {
//		return new FlatXmlDataSetBuilder().build(new File("dataset.xml"));
		return new XlsDataSet(new File("dataset.xls"));
	}

	private void cleanlyInsert(IDataSet dataSet) throws Exception {
		IDatabaseTester databaseTester = new JdbcDatabaseTester(JDBC_DRIVER, JDBC_URL, USER, PASSWORD);
		databaseTester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT);
		databaseTester.setDataSet(dataSet);
		databaseTester.onSetup();
	}

	@Test
	public void findsAndReadsExistingPersonByFirstName() throws Exception {
		PersonRepository repository = new PersonRepository(dataSource());
		Person charlie = repository.findPersonByFirstName("Charlie");

		assertThat(charlie.getFirstName(), is("Charlie"));
		assertThat(charlie.getLastName(), is("Brown"));
		assertThat(charlie.getAge(), is(42));
	}

	@Test
	public void returnsNullWhenPersonCannotBeFoundByFirstName() throws Exception {
		PersonRepository repository = new PersonRepository(dataSource());
		Person person = repository.findPersonByFirstName("iDoNotExist");

		assertThat(person, is(nullValue()));
	}

	private DataSource dataSource() {
		JdbcDataSource dataSource = new JdbcDataSource();
		dataSource.setURL(JDBC_URL);
		dataSource.setUser(USER);
		dataSource.setPassword(PASSWORD);
		return dataSource;
	}

}
```

```groovy:build.gradle
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'provided-base'

buildscript {
    repositories {  
        maven { url 'http://jcenter.bintray.com' }
    }
    dependencies {
        classpath 'com.netflix.nebula:gradle-extra-configurations-plugin:1.12.+'
    }
}
 
sourceCompatibility = "$project.java_version"
targetCompatibility = "$project.java_version"
 
version = "$project.spec_version"
group = "$project.app_group"
 
repositories {
    maven { url "http://repo.maven.apache.org/maven2" }    
}
 
dependencies {
    provided 'org.projectlombok:lombok:1.16.+'

    compile("com.h2database:h2:1.4.+")

	// Logging
	compile('ch.qos.logback:logback-classic:1.1.+')
	compile('org.slf4j:slf4j-api:1.7.+')
	compile('org.slf4j:jcl-over-slf4j:1.7.+')
	compile('org.slf4j:log4j-over-slf4j:1.7.+')
    
    testCompile 'junit:junit:4.11', {
        transitive = false
    }
    testCompile 'org.hamcrest:hamcrest-all:1.3'
    testCompile 'org.dbunit:dbunit:2.5.+'
}
 
def defaultEncoding = 'UTF-8'
tasks.withType(JavaCompile) {
    options.encoding = defaultEncoding
}
 
jar {
    manifest {
        attributes 'Implementation-Title': "$project.app_description",
//                   'Main-Class': "org.gradle.Main",
                   'Implementation-Version': version + "-$project.patch_version"
    }
}
 
clean {
    delete 'bin', 'target', 'logs'
}

task(runMain, dependsOn: 'classes', type: JavaExec) {
    main = 'org.gradle.Main'
    classpath = sourceSets.main.runtimeClasspath
    standardInput= System.in
}
 
defaultTasks 'runMain'

```

```sql:schema.sql
create table if not exists PERSON (
	ID int identity primary key,
	NAME varchar,
	LAST_NAME varchar,
	AGE  smallint,
);
```

```xml:dataset.xml
<dataset>
  <PERSON NAME="Bob" LAST_NAME="Doe" AGE="18"/>
  <PERSON NAME="Alice" LAST_NAME="Foo" AGE="23"/>
  <PERSON NAME="Charlie" LAST_NAME="Brown" AGE="42"/>
</dataset>
```

## 参考

* [Database Tests With DbUnit (Part 1)](http://www.marcphilipp.de/blog/2012/03/13/database-tests-with-dbunit-part-1/)
* [GitHub: marcphilipp/dbunit-examples](https://github.com/marcphilipp/dbunit-examples)