[Spring] ユニットテスト実行前の 1 回だけ SQL を実行したい
Spring Boot + JUnit5 のユニットテストで、テストファイル実行時に最初の 1 回だけ SQL を実行する方法。複数のテストメソッドがあるテストファイルで @Sql
を使うと、テストメソッドごとに @Sql
で指定した SQL が実行される。 1 テストファイルにつき 1 回だけテストデータを流す方法を調べたのでメモ。
ResourceDatabasePopulator を使う
@BeforeAll
で ResourceDatabasePopulator
を使って、スクリプトを実行する。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import javax.sql.DataSource;
@SpringBootTest
public class ResourceDatabasePopulatorTest {
@BeforeAll
public static void initDb(@Autowired DataSource dataSource) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
// テスト用の SQL ファイルを指定する
populator.addScript(new ClassPathResource("xxx.sql"));
populator.addScript(new ClassPathResource("xxxx.sql"));
populator.execute(dataSource);
}
}
@BeforeAll
は static
だからフィールドインジェクションはできない。だから、メソッドの引数のとこで @Autowired
した。
@BeforeAll
で SQL 実行しているので、テストクラス内で 1 番最初に 1 回だけ実行される。テスト終了後はロールバックされ、 DB データ投入前の状態に戻る。
@Sql はテストメソッドごとに実行される
テストクラス 1 つにつき 1 回だけ SQL を実行したかった理由。
ID
指定した同じデータを各メソッドで使いたいときがあったから (DELETE
できるできないとかトランザクションの確認とかで )- 同じ
ID
のデータは一意制約違反になったから
例えば以下のように、テストクラス前に @Sql
を付けると、各テストメソッド実行前に同じ SQL が実行される。
@SpringBootTest
@Sql("classpath:unit/test.sql")
public class ResourceDatabasePopulatorTest {
}
各メソッド前に @Sql
を付けると、各メソッド後にデータは消えるが、同じ ID
で SQL 流すと一意制約違反になる。
@SpringBootTest
public class ResourceDatabasePopulatorTest {
@Test
@Sql("classpath:unit/test.sql")
public void testXxx() {
}
@Test
@Sql("classpath:unit/test.sql")
public void testXyz() {
}
}
推測だが、これはロールバックはされる (データは消える) が、コミットされない、または同一トランザクション内だから auto_increment
はリセットされていない (今回使用していたのは MySQL )。実際、 MySQL はデータを削除しても、 auto_increment
の ID
は削除データ ID + 1
で次のデータが登録される。