기존 테스트 코드
- 적용 전 테스트 코드
@WebMvcTest(TodoController.class)
class TodoControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private TodoService todoService;
@Test
void todo_단건_조회에_성공한다() throws Exception {
long todoId = 1L;
String title = "title";
AuthUser authUser = new AuthUser(1L, "email", UserRole.USER);
User user = User.fromAuthUser(authUser);
UserResponse userResponse = new UserResponse(user.getId(), user.getEmail());
TodoResponse response = new TodoResponse(
todoId,
title,
"contents",
"Sunny",
userResponse,
LocalDateTime.now(),
LocalDateTime.now()
);
when(todoService.getTodo(todoId)).thenReturn(response);
mockMvc.perform(get("/todos/{todoId}", todoId))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(todoId))
.andExpect(jsonPath("$.title").value(title));
}
@Test
void todo_단건_조회_시_todo가_존재하지_않아_예외가_발생한다() throws Exception {
long todoId = 1L;
when(todoService.getTodo(todoId))
.thenThrow(new InvalidRequestException("Todo not found"));
mockMvc.perform(get("/todos/{todoId}", todoId))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(HttpStatus.OK.name()))
.andExpect(jsonPath("$.code").value(HttpStatus.OK.value()))
.andExpect(jsonPath("$.message").value("Todo not found"));
}
}
- Spring Security Filter Chain이 없음
- 요청이 그대로 Controller까지 도달
Spring Security를 도입 결정한 후의 테스트 코드
문제
해결 과정
1. Bean 오류
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in org.example.expert.config.JwtFilter required a bean of type 'org.example.expert.config.JwtUtil' that could not be found.
Action:
Consider defining a bean of type 'org.example.expert.config.JwtUtil' in your configuration.
@MockBean
private JwtUtil jwtUtil;
2. 인증 오류
문제
- 위의 방식으로 해도 테스트 코드가 성공
- 인증된 사용자가 요청하는 상황을 만드는 테스트로 변경
해결 과정
1. 커스텀 어노테이션 생성
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@WithSecurityContext(factory = WithMockAuthUserSecurityContextFactory.class)
public @interface WithMockAuthUser {
long userId() default 1L;
String email() default "email@email.com";
String nickname() default "nickName";
UserRole role() default UserRole.USER;
}
- 테스트 실행 전 SecurityContext를 설정
2. SecurityContext를 만드는 Factory 생성
public class WithMockAuthUserSecurityContextFactory implements WithSecurityContextFactory<WithMockAuthUser> {
@Override
public SecurityContext createSecurityContext(WithMockAuthUser annotation) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
AuthUser authUser = new AuthUser(
annotation.userId(),
annotation.email(),
annotation.nickname(),
annotation.role()
);
List<SimpleGrantedAuthority> authorities = List.of(new SimpleGrantedAuthority("ROLE_" + annotation.role().name()));
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(authUser, null, authorities);
context.setAuthentication(authenticationToken);
return context;
}
}
- 실제 JwtFilter가 하는 역할을 테스트에서 대신 수행
3. 완성된 TestCode
@WebMvcTest(TodoController.class)
@Import({SecurityConfig.class, JwtUtil.class, CustomAuthenticationEntryPoint.class, CustomAccessDeniedHandler.class})
class TodoControllerTest {
...
@Test
@WithMockAuthUser(userId = 1L, email = "email", nickname = "nickname", role = UserRole.USER)
void todo_단건_조회에_성공한다() throws Exception {
}
}