개발환경
- Spring Boot 3.2.3
- JDK 17
- Gradle 8.5
- queryDsl 5.0.0
개요
- JPA 테스트 및 @Repository 어노테이션을 사용한 클래스의 DB 계층 단위 테스트가 목표였습니다.
- TestDB만을 위한 환경을 구축하고 싶었습니다.
1. 문제점
@ExtendWith(SpringExtension.class)
@DataJpaTest
@ActiveProfiles(value = "test")
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class RepositoryTest {
@Autowired
MemberRepository memberRepository;
@Test
public void test(){
memberRepository.findById(1L);
}
}
- @Repository 어노테이션으로 작성된 memberRepository클래스의 빈을 생성하지 못하는 에러가 발생했습니다.
1 - 1. Error
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.ruu.bootthymeleafjpa.study.RepositoryTest': Unsatisfied dependency expressed through field 'memberRepository': No qualifying bean of type 'org.ruu.bootthymeleafjpa.domain.MemberRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
- @DataJpaTest는 JPA만을 테스트하기 위한 단위 테스트로 JPA 환경 설정에 특화된 스프링 테스트컨텍스트를 가지고 있다고 합니다.
- 이 때, 빈을 로드하는데 @Repository, @Controller, @Service 등의 스프링 빈을 컨테이너에 등록하지 않습니다.
- 이러한 이유로 @Repository라는 어노테이션을 찾지 못해서 빈의 생성자를 정의하지 못한 것입니다.
1 - 2. 해결
@DataJpaTest(includeFilters =
@ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Repository.class))
- DataJpaTest에 컴포넌트 스캔으로 @Repository 어노테이션을 찾고 정보를 읽어 빈 생성이 가능하도록 필터를 추가했습니다.
2. 최종 코드
2 - 1. Annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Repository;
import org.springframework.test.context.ActiveProfiles;
@DataJpaTest(includeFilters =
@ComponentScan.Filter(type =
FilterType.ANNOTATION,
classes = Repository.class))
@ActiveProfiles(value = "test")
@AutoConfigureTestDatabase(replace = Replace.NONE)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RepositoryTest{
}
2 - 2. TestCode
@RepositoryTest
public class HelloRepository {
@Autowired
private MemberRepository memberRepository;
@Autowired
private TestEntityManager entityManager;
Long savedId;
String name;
@BeforeEach
public void setUp(){
Member member = new Member();
member.setUsername("memberA");
name = member.getUsername();
savedId = memberRepository.save(member);
entityManager.flush();
entityManager.clear();
}
@Test
public void testMember() {
Member findMember = memberRepository.findById(savedId);
Assertions.assertThat(findMember.getId()).isEqualTo(savedId);
Assertions.assertThat(findMember.getUsername()).isEqualTo(name);
}
}
3. 환경설정
test/resources/application.yml
spring:
datasource:
url: jdbc:h2:tcp: # db url
username:
password:
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: create
properties:
hibernate:
show_sql: true
format_sql: true
database-platform: org.hibernate.dialect.H2Dialect # 방언 설정
profiles:
active: test # test라는 active를 읽습니다.
참고 자료
인프런 질의 응답
스택오버플로우