[트러블 슈팅 - Spring Test] 의존성 주입 실패 (test yml 파일 안 읽힘)
2024. 8. 25. 20:35ㆍSpring
문제 사항
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'messageController' defined in file [~~~\controller\MessageController.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'messageService': Injection of autowired dependencies failed
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'messageService': Injection of autowired dependencies failed
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'naver-cloud-sms.accessKey' in value "${naver-cloud-sms.accessKey}"
- MessageController 빈 등록이 실패했다. MessageService의 의존성 주입 실패로 인한 문제이다.
- 구체적인 원인은 application-api.properties 파일에 정의된 설정 값(naver-cloud-sms.accessKey)이 읽히지 않아 @Value로 주입되는 값이 없어서 발생한 문제이다.
문제 해결
원인
- API 설정 파일이 읽히지 않음: application-api.properties 파일이 제대로 로드되지 않아 스프링이 @Value로 주입하려는 값을 찾지 못함.
- 테스트 환경에서의 프로퍼티 파일 로드 문제: 테스트 환경에서는 기본적으로 application.yml 파일 이외 파일은 읽지 못하는 문제가 있다 때문에 테스트를 위한 properties 파일 경로를 별도 설정해 읽을 수 있게 설정해 둔 다.
- 통합 테스트의 특성: @SpringBootTest는 통합 테스트로 모든 빈을 로드합니다. 이 과정에서 다른 부분(이 경우 MessageService)에서 발생한 문제 application-api.properties의 등록된 @Value 값을 읽어 오지 못한 문제가 전체 테스트에 영향을 미쳐 사용하지 않은 MessageController에서도 오류가 발생했습니다.
문제 해결
- 프로퍼티 파일 경로 명시: @TestPropertySource를 사용해 application-api.properties 파일의 경로를 명시적으로 지정했습니다. 이를 통해 테스트 시 필요한 프로퍼티 값이 올바르게 로드되도록 했습니다.
- BaseTest 추상 클래스 도입: 공통적으로 필요한 설정과 프로퍼티 파일 로드를 BaseTest라는 추상 클래스로 묶어 관리하도록 했습니다. 이로 인해 각 테스트 클래스에서 반복적으로 설정을 지정하지 않아도 되며 설정 파일 누락 문제를 방지할 수 있습니다.
@Slf4j
@RequiredArgsConstructor
@Service
@Transactional(readOnly = true)
public class MessageService {
@Value("${naver-cloud-sms.accessKey}")
private String accessKey;
@Value("${naver-cloud-sms.secretKey}")
private String secretKey;
@Value("${naver-cloud-sms.serviceId}")
private String serviceId;
@Value("${naver-cloud-sms.senderPhone}")
private String phone;
private final StoreRepository storeRepository;
private final MessageStorageRepository messageStorageRepository;
private final MessageHistoryRepository messageHistoryRepository;
}
@SpringBootTest
@TestPropertySource(locations = "classpath:application-api.properties")
@ActiveProfiles("test")
@Transactional
public abstract class BaseTest {
// 공통 설정 및 프로퍼티 파일 경로 지정
}
// 개별 테스트 클래스
@Slf4j
class MembersServiceTest extends BaseTest {
@Autowired
private MembersService membersService;
@Autowired
private MembersRepository membersRepository;
@Autowired
private BCryptPasswordEncoder encoder;
@BeforeEach
@DisplayName("테스트용 아이디 생성")
void ApprovedID() {
Members members = Members.builder()
.memberId("UserId")
.memberPassword(encoder.encode("UserPw"))
.role(Role.USER)
.build();
membersRepository.save(members);
log.info("테스트용 아이디 생성 완료");
}
@Test
@DisplayName("아이디 생성")
void createMember() {
//given
MembersDTO membersDTO = new MembersDTO("createId", "createPw");
//when
Boolean result = membersService.createMember(membersDTO);
//then
assertEquals(result, true);
}
}
왜 사용하지 않은 MessageService에서 오류가 발생했는가?
@SpringBootTest는 통합 테스트 방식으로 테스트 실행 시 전체 애플리케이션 컨테이너에 등록된 context를 로드합니다. 이 과정에서 MessageService와 같은 다른 빈들도 함께 로드되므로 그 빈에서 발생한 문제가 전체 context 로딩 실패로 이어져 테스트 실행이 중지될 수 있습니다.
결론
@SpringBootTest 사용 시 주의사항: 통합 테스트에서 전체 빈이 로드되므로 테스트와 직접적으로 관련 없는 빈에서 발생한 문제도 테스트에 영향을 줄 수 있습니다. 이를 방지하기 위해 @MockBean을 사용하거나, 필요한 빈만 로드하는 테스트 방@WebMvcTest을 사용하는 방법이 있습니다.
'Spring' 카테고리의 다른 글
[트러블 슈팅 - 중복 API 요청] API 멱등성 (feat: redis) 해결 완료 (0) | 2024.09.11 |
---|---|
[MockMvc - SpringTest] Spring Security Login Test (0) | 2024.08.25 |
Spring Security와 Redis 이용한 로그인 세션 유지 (0) | 2024.08.14 |
Github Action 이용한 CI/CD Spring Server 자동화 배포 (feat: Synology Nas) (0) | 2024.06.05 |
[Spring - Security] OAuth2 클라이언트와 Security 기초 인증 / 인가 처리 (Feat - Kakao Login && Local Login) - 2 (0) | 2024.05.19 |