Spring BootのテストでJUnit5+H2+@DataJpaTestアノテーションでテストする方法

Spring BootのテストでJUnit5+H2+@DataJpaTestアノテーションでテストする方法

リポジトリインタフェースのテストでDBSetup,DBUnitあたりを使うことが多いと思うのですが、H2 + @DataJpaTestアノテーションを使うことによってデーたベースをインメモリにすることができます。

H2 + @DataJpaTestアノテーションのメリットは以下の通りです。

ライブラリ メリット デメリット
DBSetup 超流行っている 各開発者のテストデータによって他の開発者のテストケースが失敗する恐れがある
H2 インメモリデータベースなので閉じられたデータベースでのテストとなる ネイティブクエリを使用している場合、H2と構文が異なる場合、使えない恐れがある

build.gradleのdependenciesです。

dependencies {
  // 省略
  testImplementation('org.springframework.boot:spring-boot-starter-test') {
    exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
  }
  testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.2.0'
  testImplementation 'org.assertj:assertj-core:3.15.0' // for assertion
  testImplementation 'com.h2database:h2:1.4.200' // for in memory database
}

リポジトリクラスのテストコードサンプルです。

package jp.co.confrage.repository;

import java.util.List;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

import com.example.demo.primary.entity.EmpUser;

@DataJpaTest
public class EmpUserRepositoryTest {

  @Autowired private TestEntityManager em;
  @Autowired private EmpUserRepository repository;

  @BeforeEach
  void beforeEach() { // ここでエンティティクラスを使用してH2にデータ作成する
    em.persist(new EmpUser("STOF", 20));
    em.persist(new EmpUser("ETHOSENS", 30));
    em.persist(new EmpUser("dulcamara", 40));
  }

  @Test
  public void サンプルテスト() {
    List list = repository.findByHogehoge();
    Assertions.assertThat(list.size()).isEqualTo(3);
  }
}

@DataJpaTestアノテーション

テストクラスに対して@DataJpaTestアノテーションをつけます。

これで、インメモリDBの設定をしたり@Entity,@Repositoryアノテーションが付加されたクラスやインタフェースをBean登録されます。

@ExtendWith(SpringExtension.class)アノテーションは、@DataJpaTestアノテーションに含まれているため不要です。

インメモリデータベースを使用することにより、各テストの前処理でtruncateなどの処理が不要になりますのでDBSetupよりも便利だと思います。

また、@DataJpaTestアノテーションは@Transactionalアノテーションを含むため、各テストごとにロールバックしてくれます。

JUnitテスト時のコンソールログにH2に接続されているログが出力されていることが確認できます。

o.s.j.d.e.EmbeddedDatabaseFactory        : Starting embedded database: url='jdbc:h2:mem:d67d43s5-42c0-464e-b78f-7feb1b46f23b;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'

参考サイト

Spring Boot @DataJpaTest - Testing JPA @Repository
Learn about Spring Boot @DataJpaTest annotation and how to use it for testing JPA repositories using TestEntityManager a...

PKがサロゲートキーの場合(PostgreSQL)

PKがサロゲートキーでシーケンスオブジェクトで採番している場合、テストのたびにシーケンスオブジェクトを初期化しないと、各テスト間でシーケンスを保持してしまうことになり、独立性が保てなくなります。

PostgreSQLの場合とH2ではシーケンスオブジェクトの初期化が違います。テストの@BeforeEachアノテーションを付与しているメソッドでTestEntityManagerからH2のネイティブクエリを発行する必要があります。

@Autowired private TestEntityManager em;

@BeforeEach
void beforeEach() {
  em.getEntityManager()
    .createNativeQuery("alter sequence mst_id_seq restart with 1 ") // シーケンスオブジェクト名:mst_id_seq
    .executeUpdate(); // シーケンスオブジェクトの採番を初期化
  em.clear(); // 管理状態をクリア
}

Entityクラス作成しておく

SQLで使っているテーブルのエンティティは全て作成しておく必要があります。そうでないとリポジトリのテストで、「テーブルが見つかりません」とエラーが出ます。

参考サイト

JUnit 5 User Guide
Testing in Java & JVM projects

コメント

タイトルとURLをコピーしました