728x90
문제
문제 상황
@Value 어노테이션을 사용하여 yml 파일의 프로퍼티 값을 secretKey 필드에 주입하려고 한다.
서버 실행시 잘 작동했지만, 테스트 수행시 secretKey 필드가 초기화되지 않았다는 에러가 발생한다.
Request processing failed; nested exception is kotlin.UninitializedPropertyAccessException: lateinit property secretKey has not been initialized
문제 분석
@Value는 Spring이 해당 클래스의 인스턴스를 생성하고 관리할때, 프로퍼티 파일(yml)에 정의된 값을 자동으로 주입한다.
하지만 @WebMvcTest와 같은 단위 테스트는 특정 Controller만 로드하기 때문에
@Value로 직접 인스턴스를 생성하거나 컨텍스트에 등록되지 않은 클래스에서는 값을 자동으로 주입받을 수 없다.
해결 방법
1. 필드에서 @Value를 통해 값을 주입받는 것이 아닌, 생성자 주입을 이용한다.
2. TestConfig에서 @Value로 값을 주입받아 해당 인스턴스를 생성한다.
3. TestConfig 클래스를 테스트 컨텍스트에 포함시켜 @Autowired로 인스턴스를 주입한다.
코드로 알아보기
기존 코드
컴포넌트
@Component
class EncryptComponent {
@Value("\${encrypt.secretKey}")
private var secretKey: String) // 주입받은 값
private val encoder = Base64.getEncoder()
private val decoder = Base64.getDecoder()
..
}
값을 @Value로 주입받는다.
단위 테스트 코드
@WebMvcTest(LoanRequestController::class)
internal class LoanRequestControllerTest {
...
private lateinit var encryptComponent: EncryptComponent // 여기서 문제 발생
...
@BeforeEach
fun init() {
generateKey = GenerateKey()
loanRequestServiceImpl = LoanRequestServiceImpl(
generateKey, userInfoRepository, encryptComponent
)
...
}
해결 코드
생성자로 값을 주입받고, TestConfig로부터 @Value로 값을 가져와서 속성 값을 가져와 반환한다.
컴포넌트 : 값을 생성자로 주입받도록 변경
@Component
class EncryptComponent(
@Value("\${encrypt.secretKey}")
private var secretKey: String
) {
private val encoder = Base64.getEncoder()
private val decoder = Base64.getDecoder()
...
testConfig에서 속성 값 주입
@Configuration
class TestConfig {
@Bean
fun encryptComponent(@Value("\${encrypt.secretKey}") secretKey: String): EncryptComponent {
return EncryptComponent(secretKey)
}
}
testConfig에서 속성값을 주입할 수 있는 이유는 testConfig는 @Configuration로 인해 Spring의 설정 클래스로 인식된다.
이 안에서 선언된 메서드는 빈 정의를 생성하며 Spring IoC 컨테이너에 의해 관리된다.
Spring은 Spring이 관리하는 빈들 중 @Value 어노테이션이 붙을 경우 값 주입을 수행한다.
테스트 컨텍스트에 추가
@WebMvcTest(LoanRequestController::class)
@ContextConfiguration(classes = [TestConfig::class]) // 추가
internal class LoanRequestControllerTest {
@Autowired
private lateinit var encryptComponent: EncryptComponent
// init()에서 encryptComponent 부분 삭제
@BeforeEach
fun init() {
generateKey = GenerateKey()
loanRequestServiceImpl = LoanRequestServiceImpl(
generateKey, userInfoRepository, encryptComponent
)
}
728x90
'문제&해결' 카테고리의 다른 글
Builder 패턴 객체 생성시 필드 초기값 무시되는 문제 (0) | 2024.03.17 |
---|---|
[Test] 데이터 의존성을 줄인 통합테스트 코드 작성 (MockUser, Spring Security) (0) | 2024.03.17 |
도커 컨테이너의 ip 주소는 왜 127.0.0.1(localhost)가 아닐까? (0) | 2024.03.09 |
로컬, docker 컨테이너 포트 충돌 : 포트 관리 중요성 (mysql) (0) | 2024.03.09 |
웹소켓 취약점 해결 : 인증 도입 (STOMP, Jwt, Spring Security) (3) | 2024.02.20 |