Spring Boot, Spock, DbSetup でトランザクションを使ったテスト

以前のポスト [Spring Boot, Spock, DBUnit でトランザクションを使ったテスト](http://qiita.com/yo1000/items/0ce7e8ab0800b49861f7) でも触れたように DbSetup では、本日現在 (2016-12-11)、[テストデータのセットアップ処理でコミット処理まで実施してしまう作り](https://github.com/Ninja-Squad/DbSetup/blob/master/DbSetup-core/src/main/java/com/ninja_squad/dbsetup/DbSetup.java#L108)になっている。

リファレンスを確認しても、`DbSetup#launch()` を使用したテストデータのセットアップ方法にしか触れられていない。
http://dbsetup.ninja-squad.com/user-guide.html

ただし、ソースを確認すれば、`DbSetup#launch()` によるセットアップ処理は非常にシンプルなものであり、問題のコミット処理についてもこれを避けた記述が可能であることがわかるため、今回は DbSetup を使用した場合の、トランザクションを有効にしたテストの記述方法を書き残しておく。

# 環境

- Java 1.8.0_91
- Maven 3.3.9 (Maven wrapper)
- Spring Boot 1.4.0.RELEASE
- Groovy 2.4.7
- Spock 1.1-groovy-2.4-rc-3
- DbSetup 2.1.0

```
$ ./mvnw -version
Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T01:41:47+09:00)
Maven home: /Users/yo1000/.m2/wrapper/dists/apache-maven-3.3.9-bin/2609u9g41na2l7ogackmif6fj2/apache-maven-3.3.9
Java version: 1.8.0_91, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/jre
Default locale: ja_JP, platform encoding: UTF-8
OS name: "mac os x", version: "10.11.5", arch: "x86_64", family: "mac"
```

# 依存関係

Spring Boot によるテスト、Spock、DBUnit それぞれを動作させるために必要なものを一通り挙げると以下のような感じ。

```xml:pom.xml&nbsp;(必要箇所のみ抜粋)
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <version>1.4.0.RELEASE</version>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>org.codehaus.groovy</groupId>
  <artifactId>groovy-all</artifactId>
  <version>2.4.7</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.spockframework</groupId>
  <artifactId>spock-core</artifactId>
  <version>1.1-groovy-2.4-rc-3</version>
  <scope>test</scope>
</dependency>
  <dependency>
  <groupId>org.spockframework</groupId>
  <artifactId>spock-spring</artifactId>
  <version>1.1-groovy-2.4-rc-3</version>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>com.ninja-squad</groupId>
  <artifactId>DbSetup</artifactId>
  <version>2.1.0</version>
  <scope>test</scope>
</dependency>
```

```xml:pom.xml&nbsp;(必要箇所のみ抜粋)
<plugin>
  <groupId>org.codehaus.gmavenplus</groupId>
  <artifactId>gmavenplus-plugin</artifactId>
  <version>1.5</version>
  <executions>
    <execution>
      <goals>
        <goal>addTestSources</goal>
        <goal>testCompile</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <sources>
      <fileset>
        <directory>${pom.basedir}/src/test/groovy</directory>
        <includes>
          <include>**/*.groovy</include>
        </includes>
      </fileset>
    </sources>
  </configuration>
</plugin>
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.19.1</version>
  <configuration>
    <includes>
      <include>**/*Test.java</include>
      <include>**/*Spec.java</include>
      <include>**/*Tests.java</include>
      <include>**/*Specs.java</include>
    </includes>
  </configuration>
</plugin>
```

# テスト

ここからは実際の書き方について。行うことは以下の3つです。

1. `TransactionAwareDataSourceProxy` を使用して `DataSource` をラップする。
1. テスト終了時にロールバックしたいテストケースに `@Transactionl` を指定する。
1. テストデータの セットアップに __`DbSetup#launch()` を使用せず、`Operation#execute(Connection, BinderConfiguration)` を使用する。__

その他は、至って普通のテストです。以下のような使い方になるかと思います。

```java:Application.java
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    @Autowired
    public DataSource dataSource(DataSourceProperties dataSourceProperties) {
        DriverManagerDataSource dataSource = new DriverManagerDataSource(
                dataSourceProperties.getUrl(),
                dataSourceProperties.getUsername(),
                dataSourceProperties.getPassword()
        );

        dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        dataSource.setSchema(dataSource.getSchema());

        return new TransactionAwareDataSourceProxy(dataSource);
    }
}
```

```groovy:TransactionalServiceSpec.groovy
@Unroll
@SpringBootTest
class TransactionalServiceSpec extends Specification {
    @Autowired
    DataSource dataSource

    @Transactional
    def "テスト終了時にテストデータとテスト対象による変更をロールバック"() {
        setup:
        // テストデータの挿入
        def destination = new DataSourceDestination(dataSource)
        def operation = Operations.insertInto(..)

        /*
         * ここが通常の DbSetup とは異なる書き方
         */
        def connection = destination.connection;
        connection.autoCommit = false
        operation.execute(connection, DefaultBinderConfiguration.INSTANCE)

        when:
        // テスト対象を実行

        then:
        // テーブルの内容を検証
        assert ..
    }

    @Transactional
    def "テスト終了時にテスト対象による変更をロールバック"() {
        when:
        // テスト対象を実行

        then:
        // テーブルの内容を検証
        assert ..
    }
}
```

以上のような書き方で、DbSetup でも、トランザクションを有効にしたテストの記述と行うことができるようになります。

やむなくデータベースに蓄積されたデータに依存したテストを書かなければならない場合などには、データの保全はとても重要になってくるため、このような書き方があることは抑えておきたいところですね。