01.02 TIL - Spring Security 포함된 테스트

이서준·2026년 1월 2일

TIL

목록 보기
24/24

기존 테스트 코드

  • 적용 전 테스트 코드
    @WebMvcTest(TodoController.class)
    class TodoControllerTest {
    
        @Autowired
        private MockMvc mockMvc;
    
        @MockBean
        private TodoService todoService;
    
        @Test
        void todo_단건_조회에_성공한다() throws Exception {
            // given
            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
            when(todoService.getTodo(todoId)).thenReturn(response);
    
            // then
            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 {
            // given
            long todoId = 1L;
    
            // when
            when(todoService.getTodo(todoId))
                    .thenThrow(new InvalidRequestException("Todo not found"));
    
            // then
            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.
  • 해결 - JwtUtil MockBean 추가
@MockBean
private JwtUtil jwtUtil;

2. 인증 오류

  • 문제
    Status
    Expected :200
    Actual   :401
  • 해결 - @WithMockUser() 어노테이션 추가

문제

  • 위의 방식으로 해도 테스트 코드가 성공
  • 인증된 사용자가 요청하는 상황을 만드는 테스트로 변경

해결 과정

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 {
		//given
		//when
		//then
	}
}
profile
Allons-y

0개의 댓글