일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Thread Library
- custom annotation
- django-crontab
- 문자열 불변성
- ReflectUtils
- 롬복주의점
- 프로세스
- task_struct
- hiberbate
- AOP
- python-socketio
- java.util.function
- 운영체제
- Thread Multiplexing
- FunctionalInterface
- rainbow table
- Spring
- @Header
- 쓰레드 라이브러리
- OS
- 도커
- Process
- SystemCall
- 문자열 리터럴
- sql-mappler
- strict stubbing
- 함수형 인터페이스
- spring-data-jpa
- none이미지
- functional interface
- Today
- Total
JH's Develog
[Spring] ReflectUtils로 클래스 멤버에 접근하기 + 테스트 코드 리팩토링에 적용 본문
ReflectUtils란?
ReflectUtils는 Spring에서 제공하는 유틸리티 클래스이며, Java Bean 형식의 클래스에 대해서 자바의 Reflection API를 쉽게 사용할 수 있게 도와줍니다.
ReflectUtils를 사용하면 자바의 Reflection API를 직접 사용하지 않고도 Java Bean 형식 클래스 멤버의 이름, 값에 손쉽게 접근할 수 있습니다.
getConstructor(Class type, Class[] parameterTypes) 메서드를 통해 Constructor 객체를 얻어와서 생성자에 접근하거나,
getBeanProperties(), getBeanGetters(), getBeanSetters() 중 하나를 사용해서 해당 객체의 모든 프로퍼티에 대한 PropertyDescriptor를 얻어올 수 있습니다.
위 코드에서 확인 할 수 있듯이 getBeanProperties(), getBeanGetters(), getBeanSetters()는 적용할 객체에 대해 읽기/쓰기를 허용할 것인지에 따라 구분됩니다.
직접 사용해보기
Controller 통합테스트를 진행하며 이미지 전송을 위한 multipart/form-data API의 테스트를 수행해야했는데,
요청을 Mocking하기 위한 MockMultipartHttpServletRequestBuilder.multipart()는 아래 코드처럼
빌더에다가 키 값마다 일일히 .param("key", "value")를 덧붙여 줘야 했습니다.
@Test
void test() throws Exception{
//Given
//노트생성요청 DTO 생성
NoteCreateRequest noteCreateRequest = createNormalNoteCreateRequest();
//전송할 이미지에 대한 MockFile생성
List<MockMultipartFile> images = createMockMultipartFiles(2);
//When & Then
mvc.perform(
multipart("/api/note")
.file(images.get(0)).file(images.get(1))
.param("notebookId", noteCreateRequest.getNotebookId().toString())
.param("whiskeyId", noteCreateRequest.getWhiskeyId().toString())
.param("whiskeyName", noteCreateRequest.getWhiskeyName())
.param("distiller", noteCreateRequest.getDistiller())
.param("price", noteCreateRequest.getPrice().toString())
.param("rating", noteCreateRequest.getRating().toString())
.param("age", noteCreateRequest.getAge().toString())
.param("nose", noteCreateRequest.getNose())
.param("taste", noteCreateRequest.getTaste())
.param("finish", noteCreateRequest.getFinish())
.param("description", noteCreateRequest.getDescription())
.param("whiskeyColor", noteCreateRequest.getWhiskeyColor().toString())
.param("smokey", noteCreateRequest.getSmokey().toString())
.param("peaty", noteCreateRequest.getPeaty().toString())
.param("herbal", noteCreateRequest.getHerbal().toString())
.param("briny", noteCreateRequest.getBriny().toString())
.param("vanilla", noteCreateRequest.getVanilla().toString())
.param("fruity", noteCreateRequest.getFruity().toString())
.param("floral", noteCreateRequest.getFloral().toString())
.param("woody", noteCreateRequest.getWoody().toString())
.param("rich", noteCreateRequest.getRich().toString())
.param("spicy", noteCreateRequest.getSpicy().toString())
.param("sweet", noteCreateRequest.getSweet().toString())
.param("salty", noteCreateRequest.getSalty().toString())
.header(JwtProperties.KEY_NAME, token)
).andExpect(status().isOk());
}
문제는 요청 DTO의 프로퍼티가 많았고, 프로젝트 초기라서 해당 DTO가 자주 변경될 가능성이 높았기 때문에 위와 같은 코드는 테스트 코드를 유지보수하기 매우 힘들었습니다.
그래서 ReflectUtils를 적용해 위 코드를 아래와 같이 리팩토링하였습니다.
@Test
void refactoredTest() throws Exception{
//Given
//노트생성요청 DTO 생성
NoteCreateRequest noteCreateRequest = createNormalNoteCreateRequest();
//전송할 이미지에 대한 MockFile생성
List<MockMultipartFile> images = createMockMultipartFiles(2);
//When & Then
MockMultipartHttpServletRequestBuilder multipartRequest = multipart("/api/note");
mvc.perform(
createMultiPartRequest(multipartRequest, noteCreateRequest, images)
).andExpect(status().isOk());
}
private MockHttpServletRequestBuilder createMultiPartRequest(
MockMultipartHttpServletRequestBuilder multipartRequest,
NoteCreateRequest noteCreateRequest,
List<MockMultipartFile> images
){
//Request에 MockFile 먼저 추가
for(MockMultipartFile image : images){
multipartRequest.file(image);
}
//Request에 DTO의 모든 속성,값 정보 추가
final PropertyDescriptor[] getterDescriptors = ReflectUtils.getBeanGetters(NoteCreateRequest.class);
for(PropertyDescriptor pd : getterDescriptors){
try{
//파라미터로 전달한 인스턴스에 대해 getter함수를 호출한 값 얻어옴
String value = String.valueOf(pd.getReadMethod().invoke(noteCreateRequest));
if(!value.equals("null")){
multipartRequest.param(pd.getName(), value);
}
}catch (Exception e){
e.printStackTrace();
}
}
return multipartRequest;
}
- 재사용을 위해 Private 메서드를 분리함
- DTO정보를 읽기만 하면 되므로 ReflectUtils의 getBeanGetter() 사용함
- PropertyDescriptor를 순회하면서 DTO의 모든 프로퍼티에 대한 키와 값을 multipart 요청의 파라미터로 추가함
'Spring' 카테고리의 다른 글
[Spring] Mockmvc로 multipart/form-data 테스트하기 (0) | 2022.05.26 |
---|---|
[Spring Security] Filter 에서 발생한 예외 핸들링하기 (1) | 2022.05.20 |
[Spring] Mockito의 Strict Stubbing과 lenient의 차이는? (0) | 2022.05.12 |
[Spring Security] HandlerMethodArgumentResolver로 Authentication에서 Principal 정보 가져오기 (0) | 2022.04.14 |
[SpringBoot] Entity 속성정보 상속받기 (0) | 2022.04.07 |