SpringBoot 단위테스트 @DataJpaTest 오류 해결

SeHun.J·2024년 5월 11일

@DataJpaTest

@DataJpaTest
public class JpaTest {
}

JPA Repository를 테스트하기 위한 테스트코드를 작성하기 위해서 @DataJpaTest라는 어노테이션이 필요하다는 것을 알게 되었습니다.

DataJpaTest 어노테이션?
Spring에서 JPA 관련 테스트 설정만 로드한다. DataSource의 설정이 정상적인지, 제대로 생성 수정 삭제 조회 하는지 등의 테스트가 가능하다.
출처: https://0soo.tistory.com/40 [Lifealong:티스토리]

org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource 
[org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]:
Unsatisfied dependency expressed through method 'dataSourceScriptDatabaseInitializer' parameter 0:
Error creating bean with name 'dataSource':
Failed to replace DataSource with an embedded database for tests.
If you want an embedded database please put a supported one on the classpath or
tune the replace attribute of @AutoConfigureTestDatabase.

그런데 다음과 같은 오류가 발생했습니다. 대충 읽어보면, dataSource와 관련해서 문제가 생긴 것 같네요. 그리고 이건 곧 DB와 관련된 문제겠네요. 저는 MySQL을 사용하고 있고 테스트도 이걸로 진행하고 있습니다.

문제해결

관련 키워드로 검색하니 스택오버플로우에서 관련 질문글을 금방 찾을 수 있었습니다.
@DataJpaTest를 사용할 경우, Default로 EmbededDatabase (H2)를 사용하도록 되어 있다고 하네요. 하지만 저는 application.yml에서도 MySQL로 설정했었기 때문에 관련 DB를 찾을 수 없어서 오류가 난 케이스입니다.

  1. 어노테이션 추가하기
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class JpaTest {
}

@AutoConfigureTestDatabase 어노테이션을 추가로 달아서, 기본값을 사용하지 않도록 설정합니다. 그러면 application.yml에 설정되어 있는 DB를 읽어서 연결처리를 할테니 문제가 해결됩니다.

혹은 Test용 설정이 따로 있는 경우, 아래와 같이 프로필을 추가하고 활성화해도 됩니다.

@DataJpaTest
@ActiveProfiles("test")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class JpaTest {
}
  1. application.yml에 설정 추가하기
spring:
  test:
    database:
      replace: none

혹은 application.yml에 위와 같이 replace 옵션을 none으로 하는 방법도 있습니다.

Autowired가 안되는 이유

@DataJpaTest를 사용하는 단위테스트에서는 당연하게도 @Autowired를 통한 주입을 받을 수 없습니다. @SpringBootTest를 통한 통합테스트가 아니고, 그렇기에 Component Scan을 하지 않아 컨테이너에 @Component 빈들이 등록되지 않습니다.

단위테스트에서는 @BeforeAll이나 @BeforeEach를 활용하여 테스트케이스가 실행되기 전에 필요한 컴포넌트를 직접 생성하여 사용해야 합니다.

@DataJpaTest
@ActiveProfiles("test")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class JwtServiceTest {
	@Autowired
    private UserRepository userRepository;
    private static JwtService jwtService;
    private static BCryptPasswordEncoder bCryptPasswordEncoder;
    
    @BeforeAll
    public static void setUp() {
        jwtService = new JwtService();
        // @Value는 IOC 컨테이너를 거치지 않았기 때문에, ReflectionTestUtils를 통해 강제로 주입.
        ReflectionTestUtils.setField(jwtService, "secret", "secretkey");
        ReflectionTestUtils.setField(jwtService, "accessTokenExpiration", 3600);
        ReflectionTestUtils.setField(jwtService, "refreshTokenExpiration", 86400);
        jwtService.init();
        bCryptPasswordEncoder = new BCryptPasswordEncoder();
    }
    
    @Test
    @DisplayName("유저정보를 기반으로 생성된 토큰을 검증")
    void JwtTest() {
    	...
    }
}

참고로, @DataJpaTest에는 @Transactional 어노테이션이 포함되어 있습니다. JUnit을 통한 Test에서 @Transactional은 SELECT를 제외한 모든 쿼리는 Rollback 대상으로 취급합니다.
그래서 테스트에서 건드린 내용은 커밋되지 않고, 롤백됩니다.

profile
취직 준비중인 개발자

0개의 댓글