【記事概要】
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 テストでは、
SpringBoot公式TestEntityManager
[GitHub] (英語) Bean を挿入することもできます。これは、テスト用に特別に設計された標準の JPAEntityManager
の代替手段を提供します。
※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.emがnullとなり、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のダイレクトメッセージでご連絡ください。