DBUnitとH2 Databaseを使ってみた。

JUnit実践入門 ~体系的に学ぶユニットテスト技法」を参考にしてDAOのテストケースを作成したのでメモ。
JunitをベースにDBUnit、H2 Databaseを使ってテストしました。
本番で稼働させているのはMySQLなんですが、単体テストのときはテスト環境に依存しないようにH2 Databaseを使用しようかなと。

H2 Databaseとは?
詳しくは こちら(wiki) 。
Pure Javaでjarファイル1つあれば動きます。データはファイルで保存されます。SQLiteみたい。
早いとの触れ込みですが、実際のところはどうなのでしょうか?あんまり使われているという話は聞かないですね。
jarの入手ですが、僕はMavenを使っているので以下のように設定しました。

pom.xml

<dependency>
    <groupId>org.dbunit</groupId>
    <artifactId>dbunit</artifactId>
    <version>2.4.7</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.5.6</version>
</dependency>

最新版はこちら を参照してください。

DBUnitとは?
詳しくは こちら(公式サイト) 。
DB接続系クラスのテストフレームワークです。
こちらもMavenで以下のように設定しました。

pom.xml

<dependency>
    <groupId>org.dbunit</groupId>
    <artifactId>dbunit</artifactId>
    <version>2.4.7</version>
</dependency>

最新版はこちら を参照してください。

JUnit実践入門を参考に以下のようにクラスを作ってみました。

DbUnitTester.java

package hoge;

import java.sql.Connection;
import java.sql.DriverManager;

import org.dbunit.AbstractDatabaseTester;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.ext.h2.H2DataTypeFactory;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public abstract class DbUnitTester extends AbstractDatabaseTester implements TestRule {

    private final String connectionUrl;
    private final String username;
    private final String password;
    
    public DbUnitTester(String driverClass, String connectionUrl) {
        this(driverClass, connectionUrl, null, null);
    }
    
    public DbUnitTester(String driverClass, String connectionUrl, String username, String password) {
        this(driverClass, connectionUrl, username, password, null);
    }
    
    public DbUnitTester(String driverClass, String connectionUrl, String username, String password, String schema) {
        super(schema);
        this.connectionUrl = connectionUrl;
        this.username = username;
        this.password = password;
        assertNotNullNorEmpty("driverClass", driverClass);
        try {
            // JDBCドライバのロード
            Class.forName(driverClass);
        } catch (ClassNotFoundException e) {
            throw new AssertionError(e);
        }
    }
    
    @Override
    public IDatabaseConnection getConnection() throws Exception {
        Connection conn = null;
        if (username == null && password == null) {
            conn = DriverManager.getConnection(connectionUrl);
        } else {
            conn = DriverManager.getConnection(connectionUrl, username, password);
        }
        DatabaseConnection dbConnection = new DatabaseConnection(conn, getSchema());
        DatabaseConfig config = dbConnection.getConfig();
        config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new H2DataTypeFactory());
        
        return dbConnection;
    }
    
    protected void executeQuery(String sql) throws Exception {
        Connection conn = getConnection().getConnection();
        conn.createStatement().execute(sql);
        conn.commit();
        conn.close();
    }
    
    protected void before() throws Exception {
        
    }
    
    protected void after() throws Exception {
        
    }
    
    abstract protected IDataSet createDataSet() throws Exception;
    
    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            
            @Override
            public void evaluate() throws Throwable {
                before();
                setDataSet(createDataSet());
                onSetup();
                try {
                    base.evaluate();
                } finally {
                    try {
                        after();
                    } finally {
                        onTearDown();
                    }
                }
            }
        };
    }
}

H2DatabaseServer.java
H2 Databaseを起動、停止させるクラス。

package hoge;

import java.sql.Connection;
import java.util.Properties;

import org.h2.tools.Server;
import org.h2.util.JdbcUtils;
import org.junit.rules.ExternalResource;

public class H2DatabaseServer extends ExternalResource {

    private final String baseDir;
    private final String dbName;
    private final String schemaName;
    private Server server = null;
    
    public H2DatabaseServer(String baseDir, String dbName, String schemaName) {
        this.baseDir = baseDir;
        this.dbName = dbName;
        this.schemaName = schemaName;
    }
    
    @Override
    protected void before() throws Throwable {
        // DBサーバの起動
        server = Server.createTcpServer("-baseDir", baseDir);
        server.start();
        // スキーマの設定
        Properties props = new Properties();
        props.setProperty("user", "sa");
        props.setProperty("password", "");
        String url = "jdbc:h2:tcp://localhost/" + dbName;
        Connection conn = org.h2.Driver.load().connect(url, props);
        
        try {
            conn.createStatement().execute("CREATE SCHEMA IF NOT EXISTS " + schemaName);
        } finally {
            JdbcUtils.closeSilently(conn);
        }
    }
    
    @Override
    protected void after() {
        // DBサーバの停止
        server.shutdown();
    }
}