새로운 프로젝트에서 Query 가 들어있는 Repository 단을 Test 해야 해서 진행을 해보았다. 일반 Repository 의 모습이라 다음 정도 수준이였다.
public interface MemberRepository {
Optional<Member> findByUsername(String username);
}
@Repository
@RequiredArgsConstructor
public class MemberJpaRepository implements MemberRepository{
private final EntityManager em;
private final JPAQueryFactory queryFactory;
...
@Override
public Optional<Member> findByUsername(String username){
return Optional.ofNullable(queryFactory.selectFrom(member)
.where(member.username.eq(username))
.fetchOne());
}
}
그리고 다음과 같이 TC 를 준비해보았다.
@ActiveProfiles("test")
@DataJpaTest
public class MemberJpaRepositoryTest extends TestObjectCreator {
@Autowired
private MemberRepository memberRepository;
@Autowired
private EntityManager em;
@BeforeEach
void be(){
// member 를 미리 넣어주기 위한 가 데이터 생성
memberRepository.save();
em.flush();
em.clear();
}
...
}
일단 TC 할 준비가 되었는지 보기 위해 실행을 시킨 결과, 다음과 같은 에러가 발생하였다.
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name '~~.MemberRepository': Unsatisfied dependency expressed through field 'memberRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type '~~.MemberRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
사실 해당 에러까지 가기도 전에 갑자기
========================== CONDITIONS EVALUATION REPORT============================
이런게 시뻘겋게 떠가지고 매우 당황해서 Context 형성하는데 에러가 있는줄 알고 한참을 해멨다. 역시 에러는 로그를 잘봐야 한다... ㅋㅋㅋㅋㅋ 어쨌든 에러 로그를 보면 다 적혀있기는 하다. 말 그대로
1번 오류 : MemberRepository 주입하고 싶은데 넣을 수가 없네?
2번 오류 : MemberRepository 에 충족되지 않은 의존관계가 정의됐네?
사실 위에 적혀있는 생성자 주입이 전부이기 때문에, 에러를 보기만 해도 Query DSL 과 연관된 문제임을 바로 알아차릴 수 있어야 하는게 정상이다.
다음 블로그를 통해 알고보니 JpaQueryFactory 는 Persistence 가 제어하는 영역이 아니기 때문에 Bean 등록이 되지 않아서 발생하는 문제였다. (@DataJpaTest 는 JPA 관련 Bean 들만 등록한다) (https://jyami.tistory.com/124)
스프링을 제대로 공부했으면, DI 에 대한 문제정도는 손쉽게 해결할 줄 알아야 한다고 생각한다!!
난 스프링을 제대로 공부하지 못했나보다. 해결하는데 한참 걸렸다. 반성한다 (뭐 결국 혼자 못하고 열심히 블로그 서치..)
우선 Test 를 위한 추가적인 Bean 이 필요한 상황임이 자명하니, @TestConfiguration 을 통해 Test 를 위한 추가적인 Bean 을 등록해보자.
@TestConfiguration
public class RepositoryTestConfiguration {
@PersistenceContext
private EntityManager em;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em);
}
}
인 앱에서 JPAQueryFactory 를 넣을 때와 거의 똑같은 Configuration 을 주는 것이다. 그리고 Repository 에는 당연히 이제 될 줄 알고, 바로 진행을 해보았다. 하지만 결과는 여전히 Fail 이였고, 똑같은 오류를 트레이스 해주고 있었다.
사실 이거는 왜이런지 아직도 잘 모르겠다. 좀 더 분석해보고 알게 되면 게시물 업데이트를 해보겠다. 너무 궁금함... 아보니 QueryDslRepositorySupport 같은걸 이용하면 된다는데.. 다른 기술을 또 사용해서 해결하는건 지양해야한다.
더 찾아보니 Import Annotation 을 따로 더 해줘야 한다고 한다. 다음과 같이 해줄 수 있겠다.
@Import(RepositoryTestConfiguration.class)
@ActiveProfiles("test")
@DataJpaTest
public class MemberJpaRepositoryTest extends TestObjectCreator{
...
}
뭐 여기까진 백번 양보해서 그럴 수 있겠다 싶었다. 근데 이렇게 해도 안되었다. 더 알아보니 Import 해줄 때 사용하는 Repository 도 같이 적어줘야 한다고 한다.
@Import({RepositoryTestConfiguration.class, MemberJpaRepository.class})
@ActiveProfiles("test")
@DataJpaTest
public class MemberJpaRepositoryTest extends TestObjectCreator{
...
}
심지어 저게 또 MemberRepository.class 면 또 안된다. RepositoryTestConfiguration 에 Repository 들에 대한 DI 를 새롭게 다 해줘도 (jpaQueryFactory() 를 포함해서) 해결이 되긴 하더라. 결국 위 방식이나 TestConfiguration 에 더 추가해야 하는 방식이나,
1. @TestConfiguration 만으로는 원하는 Bean 을 컨테이너에서 찾지 못했다.
2. MemberRepository 의 구현체인 MemberJpaRepository 가 1번으로 인해 @Autowired 에 실패한다.
왜 그런지는 잘 아직 모르겠다. Test 환경이 동작하는 구조를 조금 더 파헤쳐 봐야 이유를 알 수 있을 것 같다. @TestConfiguration 이 동작하는 원리도.. !!
.. 이건 꼭 공부해보고 추가해놓겠다. 퇴근해야함..
어쨌든 결론적으로 해결을 구하고자 읽으신 분들은, 아래와 같이 해결하면 됩니다.
1. @TestConfiguration 으로 JpaQueryFactory Bean 을 추가적으로 넣어줍니다.
2. @Import() 를 통해 해당 Configuration 파일, 그리고 사용하는 Repository 파일을 직접적으로 넣어서 의존성을 부여해줍니다.
'Logging > Spring' 카테고리의 다른 글
[Spring MVC] org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 오류 (0) | 2024.01.29 |
---|---|
[JPA] OSIV 만 믿었다가 Lazy 로딩에 발등찍힌 썰 (0) | 2024.01.27 |
Spring Boot 개발하면서 요즘 드는 생각 (0) | 2023.12.13 |
[문제기록] Lombok @Getter 사용시 boolean 값 처리에 대하여 (0) | 2023.08.30 |