Java + Spring によるテストデータ管理(3)

(前回の続き)

前回まででようやく、DBアクセスに関するテスト

* テーブルを作成
* データを登録
* テスト対象クラスのインスタンスを作成
* メソッドを実行
* 結果を検証

を実現できた。しかし、とても効率が良いとは言えない。

今回は、springに用意されているテスト用のクラスを確認してみる。

JdbcTestUtils を使う
--------------------

SQLを実行するために、前回までは `JdbcTemplate#execute()` を使っていたが、
これ以外の選択肢についていろいろと試してみようと思う。まずは、JdbcTestUtils
である。これは、spring-test に含まれる機能である。これを使うと前回の例がどのように
書き換えられるか確認しよう。

```xml:src/test/resources/drop.sql
drop table sample3
```
```xml:src/test/resources/create.sql
create table sample3 (id numeric(3), str varchar(5))
```
```xml:src/test/resources/insert.sql
insert into sample3 values (3, 'c')
```

```java:src/test/java/jp/sample/JavaDBSample3Test.java
package jp.sample;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.jdbc.JdbcTestUtils;

import org.junit.runner.RunWith;
import org.junit.*;

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class JavaDBSample3Test {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    JavaDBSample3 obj;

    @Before
    public void setUp() {
        executeScript("/drop.sql", true);
        executeScript("/create.sql", false);
    }

    @Test
    public void test() {
        executeScript("/insert.sql", false);

        List<Map<String,Object>> list = obj.select();

        assertThat(list.size(), is(1));
        assertThat((BigDecimal)list.get(0).get("ID"), is(BigDecimal.valueOf(3)));
        assertThat((String)list.get(0).get("STR"), is("c"));
    }

    public void executeScript(String file, boolean continueOnError) {
        //EncodedResource resource = new EncodedResource(new ClassPathResource(file, getClass()), "UTF-8");
        Resource resource = new ClassPathResource(file, getClass());
        JdbcTestUtils.executeSqlScript(jdbcTemplate, resource, continueOnError);
    }
}
```

これで一応テスト用のテーブルスクリプトおよびデータを外出しすることができた。
スクリプトは、クラスパスが通っている src/test/resources 配下で管理する。

しかし、このJdbcTestUtilsメソッドはdeprecated となっており、リファレンスマニュアルを見ると代わりに、

`ScriptUtils.executeSqlScript()`
または、
`ResourceDatabasePopulator クラス`

を使えとなっている。ScriptUtils は、低レベルっぽいので、ResourceDatabasePopulatorを使ってみよう。

ResourceDatabasePopulator を使う
--------------------
ResourceDatabasePopulator は JdbcTemplate ではなくConnectionオブジェクトを使うらしい。もしかすると、JdbcTemplate という抽象化オブジェクト自体が古いのかもしれない。


```xml:src/test/resources/create.sql
drop table sample3
create table sample3 (id numeric(3), str varchar(5))
```
```xml:src/test/resources/insert.sql
insert into sample3 values (3, 'c')
```

```java:src/test/java/jp/sample/JavaDBSample3Test.java
package jp.sample;

import java.math.BigDecimal;
import java.sql.Connection;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.ContextConfiguration;

import org.junit.runner.RunWith;
import org.junit.*;

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class JavaDBSample3Test {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    JavaDBSample3 obj;

    @Before
    public void setUp() {
        executeScript("/create.sql");
    }

    @Test
    public void test() {
        executeScript("/insert.sql");

        List<Map<String,Object>> list = obj.select();

        assertThat(list.size(), is(1));
        assertThat((BigDecimal)list.get(0).get("ID"), is(BigDecimal.valueOf(3)));
        assertThat((String)list.get(0).get("STR"), is("c"));
    }

    public void executeScript(String file) {
        Resource resource = new ClassPathResource(file, getClass());

        ResourceDatabasePopulator rdp = new ResourceDatabasePopulator();
        rdp.addScript(resource);
        rdp.setSqlScriptEncoding("UTF-8");
        rdp.setIgnoreFailedDrops(true);
        rdp.setContinueOnError(false);

        Connection conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
        rdp.populate(conn);
    }
}
```

JdbcTestUtils との差分も示す

```diff
@@ -1,18 +1,18 @@
 package jp.sample;
 
 import java.math.BigDecimal;
+import java.sql.Connection;
 import java.util.List;
 import java.util.Map;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.io.ClassPathResource;
 import org.springframework.core.io.Resource;
-import org.springframework.core.io.support.EncodedResource;
-import org.springframework.dao.DataAccessException;
 import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.DataSourceUtils;
+import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.jdbc.JdbcTestUtils;
 
 import org.junit.runner.RunWith;
 import org.junit.*;
@@ -32,13 +32,12 @@
 
     @Before
     public void setUp() {
-        executeScript("/drop.sql", true);
-        executeScript("/create.sql", false);
+        executeScript("/create.sql");
     }
 
     @Test
     public void test() {
-        executeScript("/insert.sql", false);
+        executeScript("/insert.sql");
 
         List<Map<String,Object>> list = obj.select();
 
@@ -47,9 +46,16 @@
         assertThat((String)list.get(0).get("STR"), is("c"));
     }
 
-    public void executeScript(String file, boolean continueOnError) {
-        //EncodedResource resource = new EncodedResource(new ClassPathResource(file, getClass()), "UTF-8");
+    public void executeScript(String file) {
         Resource resource = new ClassPathResource(file, getClass());
-        JdbcTestUtils.executeSqlScript(jdbcTemplate, resource, continueOnError);
+
+        ResourceDatabasePopulator rdp = new ResourceDatabasePopulator();
+        rdp.addScript(resource);
+        rdp.setSqlScriptEncoding("UTF-8");
+        rdp.setIgnoreFailedDrops(true);
+        rdp.setContinueOnError(false);
+
+        Connection conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
+        rdp.populate(conn);
     }
 }
```

ResourceDatabasePopulator を利用した場合、`setIgnoreFailedDrops()` のお陰でdropのエラーを無視することができる。そのため、dropとcreateのスクリプトを一つにすることができた。
そういう点では使い勝手がいい。

次回は、結果をbeanで取得する方法を確認する。