SpringBoot JPAのEntityManagerを使ったJUnitテストの設定

【記事概要】

SpringBootのJPAでRepositoryを使ったJUnitテストはできておりEntityManagerでも同じだろうと高をくくっていたら苦しんだので、設定方法を解説します。

【ポイント】

  • @SpringBootTestではなく、@DataJpaTestを使う
  • オリジナルのデータベースを使う場合@AutoConfigureTestDatabaseを使う
  • EntityManagerクラスではなく、TestEntityManegerクラスを使う

【参考】

様々なサイトを調べて@DataJpaTestを使いそうなことが分かり、下記公式サイトで解決しました。

https://spring.pleiades.io/spring-boot/docs/current/reference/html/features.html#features.testing.spring-boot-applications.autoconfigured-spring-data-jpa

環境/バージョン

  • SpringBoot:2.5.5
  • JUnit:5
  • Lombok:1.18.20

前提

  • SpringBootプロジェクトが正常に動作する
  • JPAのRepositoryのJUnitテストが実行可能

解説

コード

※コードには説明用に各行にコメントで番号(※~)を振っています。実装時はコメントは削除してください。

まず、テスト実行用クラスです。

package test.repository;

import test.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

import static org.junit.jupiter.api.Assertions.*;

@DataJpaTest // ※1
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // ※2
public class EntityManagerTest {

    @Autowired
    private TestEntityManager em; // ※3

    @Test
    public void test() {
        User user = this.em.find(User.class, 1L); // ※4
        assertEquals(1L, user.getId());
    }

}

※1:JPAのアプリケーションをテストする際に必要なアノテーションです。(Repositoryのみのテスト時は不要らしいです。)
また、デフォルトでトランザクションが開始されるため、クラス内のテストメソッドの処理はテスト実行後にロールバックされます。(ロールバック不要な場合等、詳細と設定は 公式サイト をご確認ください。)

※2:オリジナルのデータベースを使用する場合に必要なアノテーションです。記述がない場合、組み込みのデータベースが使用されます。(詳細は 公式サイト をご確認ください。)

※3:公式には下記の様に記述されており、標準で使用するEntityManagerとは別にテスト用に設計されている様です。(詳細は割愛します。)

Data JPA テストでは、TestEntityManager [GitHub] (英語)  Bean を挿入することもできます。これは、テスト用に特別に設計された標準の JPA EntityManager の代替手段を提供します。

SpringBoot公式

※4:使い方は標準のEntityManagerと同様です。

下記は、今回準備したEntityです。

package test.entity;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@Table(name = "user")
@Entity
@Getter
@Setter
public class User {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(nullable = false)
  private Long id;
}

このクラスは通常のEntityのままです。

実行

あとはいつも通りテスト(今回は、EntityManagerTestクラスのtestメソッド)を実行すればOKです。

設定ミスによるエラー内容

あえてエラーが発生する様に設定を変更し、発生するエラーを確認してみます。(エラーからの逆引きにも有効です。)

1.@DataJpaTestが未設定の場合

コード編集

package test.repository;

import test.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

import static org.junit.jupiter.api.Assertions.*;

// @DataJpaTest // ※1
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class EntityManagerTest {

    @Autowired
    private TestEntityManager em;

    @Test
    public void test() {
        User user = this.em.find(User.class, 1L);
        assertEquals(1L, user.getId());
    }

}

※1:10行目の@DataJpaTestを無効(コメント)にします。

実行

テストを実行します。

java.lang.NullPointerException
	at test.EntityManagerTest.test(EntityManagerTest.java:21)
(以下略)

this.emnullとなり、21行目実行時にNullPointerExceptionが発生します。

2.@DataJpaTestが未設定かつ@SpringbootTestが設定されている場合

コード編集

package test.repository;

import test.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

import static org.junit.jupiter.api.Assertions.*;

// @DataJpaTest // ※1
@SpringBootTest // ※2
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class EntityManagerTest {

    @Autowired
    private TestEntityManager em;

    @Test
    public void test() {
        User user = this.em.find(User.class, 1L);
        assertEquals(1L, user.getId());
    }

}

※1:10行目の@DataJpaTestを無効(コメント)にします。

※2:@SpringBootTestを記述します。

実行

おそらく19行目で、emがAutowiredできずにコンパイルエラーが出ているのではないでしょうか。

構わずテストを実行してみます。

Error creating bean with name 'test.EntityManagerTest': Unsatisfied dependency expressed through field 'em'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'test.EntityManagerTest': Unsatisfied dependency expressed through field 'em'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
  (中略)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
  (以下略)

当然ですが例外になりました。内容もDI、Autowired関連のエラーです。

最後に


記事内の分かりにくい部分や誤り等は、コメントでご連絡ください。また、別記事のご要望や質問、Web制作のご依頼も大歓迎ですので、お問い合わせフォームまたはTwitterのダイレクトメッセージでご連絡ください。