Spring
- Test 과정 이모저모
Optional의 null + orElseThrow
서비스단 테스트 과정에서 Repository는 Mock으로 선언하고
ToDoCard toDoCard = toDoCardRepository.findById(id).orElseThrow(() -> new NullPointerException("해당 할일카드를 찾을 수 없습니다."));
이 코드에서 찾는 할일카드가 없는 오류를 냈어야 했다.
처음에는 null 받아올 테니까
when(toDoCardRepository.findById(id)).thenReturn(null);
Throwable exception = assertThrows(NullPointerException.class, () -> {
toDoCardService.getToDoCard(id);
});
when(toDoCardRepository.findById(id)).thenReturn(null);
이렇게 짰는데 이러면 아예 Null값 자체를 확정지어버리게 되어 오류가 발생했다.
바로 null값이 orElseThrow()를 호출할 수 없기 때문이었다.
when(toDoCardRepository.findById(id)).thenReturn(Optional.empty());
Throwable exception = assertThrows(NullPointerException.class, () -> {
toDoCardService.getToDoCard(id);
});
null을 Optioanl.empty()로 고치니까 해결되었다.
앞으로 Optional에 딸린 null값은 Optional.empty로 하면 좋을 듯!
HttpStatusCode.valueof
클라이언트에게 Response를 보낼 때 ResponseEntity에 ResponseDto, header, 상태코드를 넣어주는 방식으로 반환하는 구조로 코드를 짰다.
테스트 할 때에 Dto(payload) 부분에는 상태코드가 없기 때문에 ResponseEntity에서 직접 상태코드를 받아야 한다.
그런데 ResponseEntity.getStatusCode()는 반환형이 HttpStatusCode라는 인터페이스다.
그래서 테스트 시
assertEquals(response.getStatusCode(), 200);
라고 안일하게 적었다가는 오류가 나고 만다.
(Expected: "200 OK", Actual: "200" 이렇게 난다. 타입 자체가 다르기도 하고..)
assertEquals(response.getStatusCode(), HttpStatusCode.valueOf(200));
assertEquals(response.getStatusCode().value(), 200);
둘 중에 맛난 것으로 고쳐쓰자.
Spring Security + Controller Test
스프링 시큐리티가 적용된 채로 컨트롤러 테스트를 하려면 난감한 점이 많다.
컨트롤러 단에서는 필터 단에서의 토큰 검증이 필요가 없는데(이것 땜시 테스트 오류 발생) 필터 단에서 보내주는 UserDetails에 대한 검증은 필요하기 때문이다.(@Authentication 이 인자 부분에 달려있음!)
그래서 우리는 1차로
@WebMvcTest(
controllers = {UserController.class, ToDoCardController.class, CommentController.class},
excludeFilters = {
@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = WebSecurityConfig.class
)
}
)
excludeFilter를 통해 기존 jwt 검증 필터들을 무시해주고.(websecurityconfig에서 ignore 쓰는 방법도 있다.)
private MockMvc mvc;
@BeforeEach
public void setup() {
mvc = MockMvcBuilders.webAppContextSetup(context)
.apply(springSecurity(new MockSpringSecurityFilter()))
.build();
}
2차로 가짜 mvc 객체에 별 기능없는 커스텀필터를 적용시켜준다.
근데 진짜 별 기능없으면 안되고 UserDetails를 받아올 수 있는 최소한의 조건은 있어야 한다.
SecurityContextHolder.getContext().setAuthentication(
(Authentication) ((HttpServletRequest) req).getUserPrincipal()
);
마지막으로
private void mockUserSetup() {
String username = "dongha";
String password = "12345678";
User testUser = new User(username, password);
UserDetailsImpl testUserDetails = new UserDetailsImpl(testUser);
mockPrincipal = new UsernamePasswordAuthenticationToken(testUserDetails, "", testUserDetails.getAuthorities());
}
테스트를 위한 Principal까지 가져와주면 클라이언트에서 보내듯이 테스트를 진행할 수 있다..
@Test
@DisplayName("ToDoCardController, 할일카드 작성 기능")
void test2() throws Exception {
//given
this.mockUserSetup();
String title = "테스트용 할일카드 제목";
String contents = "테스트용 할일카드 내용";
ToDoCardRequestDto requestDto = new ToDoCardRequestDto(
title,
contents
);
String toDoCardInfo = objectMapper.writeValueAsString(requestDto);
//when - then
mvc.perform(post("/api/todocards")
.content(toDoCardInfo)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.principal(mockPrincipal)
)
.andExpect(status().isOk())
.andDo(print());
}
알고리즘
- 프로그래머스 : 조이스틱(Level 2, 구현)
느낀 점
.
'TIL' 카테고리의 다른 글
[23.12.06] (0) | 2023.12.06 |
---|---|
[23.12.05] (1) | 2023.12.06 |
[23.11.30] (1) | 2023.12.01 |
[23.11.29] (0) | 2023.11.29 |
[23.11.28] (0) | 2023.11.28 |