JPA/Spring Data JPA

[QueryDsl] @DataJpaTest 에서 @Repository 테스트하기

KAispread 2023. 3. 30. 21:23
728x90
반응형

개요

프로젝트 진행중에 QueryDsl을 사용하는 CustomRepository를 만들었다. 여러 엔티티를 JOIN하여 데이터를 조회할 예정이였기 때문에 JpaRepository에 상속하지 않고 @Repository 어노테이션을 붙여 스프링 빈으로 등록하여 사용하고 있었다. 코드는 다음과 같다.

 

Repository

@RequiredArgsConstructor
@Repository
public class AdminRepository implements ApplimentSearchRepository {
    private final JPAQueryFactory queryFactory;

    @Override
    public Optional<ApplimentMember> findApplimentMemberById(Long applyId) {
        ...
    }

    @Override
    public List<ApplimentMemberResponse> findAllApplimentMembers(final Long recruitmentId, final ApprovalStatus status) {
        ...
    }

    @Override
    public List<ApprovedMemberResponse> findAllApprovedMembers(final Long recruitmentId) {
        ...
    }

    public Long countAttend(final Long recruitmentId, final Long userId, final Attendance attendance) {
        ...
    }

    private BooleanExpression attendanceEq(Attendance attendance) {
        ...
    }

    private BooleanExpression statusEq(ApprovalStatus status) {
        ...
    }
}

public interface ApplimentSearchRepository {
    Optional<ApplimentMember> findApplimentMemberById(Long applyId);
    List<ApplimentMemberResponse> findAllApplimentMembers(final Long recruitmentId, final ApprovalStatus status);
    List<ApprovedMemberResponse> findAllApprovedMembers(final Long recruitmentId);
}

 

문제점

이전까지 Repository의 테스트는 @DataJpaTest를 사용하여 테스트 코드를 작성하고 있었는데, 문제는 @DataJpaTest의 특성으로 인해 발생했다.

@DataJpaTest의 특징은 다음과 같다.

  • JPA에 관련된 요소들만 테스트하기 위한 어노테이션으로 JPA 테스트에 관련된 설정들만 적용해준다.
  • 메모리상에 내부 데이터베이스를 생성하고 @Entity 클래스들을 등록하고 JPA Repository 설정들을 해준다. 각 테스트마다 테스트가 완료되면 관련한 설정들은 롤백된다.

@DataJpaTest는 JPA에 관련된 설정들만 적용시켜주기 때문에 @Repository로 스프링 빈으로 등록한 AdminRepository 클래스는 주입되지 않는 것이다.

따라서, 다음과 같이 AdminRepositoryTest를 작성할 경우 AdminRepository 를 주입받을 수 없기 때문에 예외가 발생한다.

@AutoConfigureTestDatabase(replace = NONE)
@DataJpaTest
class AdminRepositoryTest {
    @Autowired
    private AdminRepository adminRepository;
    
    ...
}

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.moa.domain.member.AdminRepositoryTest': Unsatisfied dependency expressed through field 'adminRepository': No qualifying bean of type 'com.moa.domain.member.AdminRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

 

해결

이 문제는 특정 스프링 빈을 주입받지 못해 발생하는 것이기 때문에 @TestConfiguration 을 사용하여 테스트에 사용되는 클래스를 직접 빈으로 등록해주면 된다.

@TestConfiguration
public class TestQueryDslConfig {
    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }

    @Bean
    public AdminRepository adminRepository() {
        return new AdminRepository(jpaQueryFactory());
    }
}
  • AdminRepositoryJPAQueryFactory 를 주입받고 있기 때문에  JPAQueryFactoryAdminRepository 를 빈으로 등록해주면 된다.

 

@AutoConfigureTestDatabase(replace = NONE)
@Import(TestQueryDslConfig.class)
@DataJpaTest
class AdminRepositoryTest {
    @Autowired
    private AdminRepository adminRepository;
    
    ...
}

이후 이 설정들을 @Import 어노테이션을 사용하여 적용 시켜주면 된다.

 

참고로 테스트에 사용되는 어노테이션들을 모아 커스텀하면 테스트 코드를 훨씬 더 깔끔하게 나타낼 수 있다.

// 테스트용 어노테이션 생성
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@AutoConfigureTestDatabase(replace = NONE)
@Import(TestQueryDslConfig.class)
@DataJpaTest
public @interface RepositoryTest {
}


// 테스트 코드
@RepositoryTest
class AdminRepositoryTest {
    @Autowired
    private AdminRepository adminRepository;
    
    ...
}

 


++) @DataJpaTest 에서 JpaAuditing 기능 활성화

@DataJpaTest 는 최소한의 빈만 불러오기 때문에 JpaAuditing 기능을 활성화하는 Configuration은 불러와지지 않는다.

따라서, 앞에서 생성한 TestQueryDslConfig 클래스에 @EnableJpaAuditing 어노테이션을 추가하자. 그럼 @CreatedDate@LastModifiedDate의 기능이 의도한대로 잘 동작할 것이다.

@EnableJpaAuditing
@TestConfiguration
public class TestQueryDslConfig {
	...
}

 

 

참고

728x90
반응형