Filter 유닛 테스트는 처음인데요

메밀·2024년 8월 30일

1. 저만 처음일수도 있어요

Controller - Service - Repository 테스트나 해봤지 Filter의 테스트 코드는 처음 해봤다.

정확히 내가 하고자 하는 것은, Access Token 유효성을 검사하는 JwtFiler와 JwtFilter에서 발생한 예외를 처리하는 JwtExceptionFilter를 검사하는 것!

알아볼 내용을 대충 정리하면 다음과 같고,

  • JwtFilter
    • 가짜 Request, 가짜 헤더 만들기
  • JwtExceptionFilter
    • 앞 필터의 예외 상황 mocking하기

결론을 정리하면 다음과 같다.

그냥 다른 레이어의 유닛 테스트와 다를 것 없음 ㅋㅋㅋㅋ

2. Access Token 검사 필터 테스트하기

우선 전체 코드

@ExtendWith(MockitoExtension.class)
public class JwtFilterTest {

    @Mock
    private TokenService tokenService;

    @InjectMocks
    private JwtFilter jwtFilter;

    @Mock
    private FilterChain filterChain;

    @Test
    @DisplayName("유효한 액세스 토큰 도착 시, 인증 정보 설정")
    public void doFilterInternal_WithValidToken_ShouldSetAuthentication() throws ServletException, IOException {
        // Given
        String validToken = "ValidToken";
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        request.addHeader(JwtFilter.AUTHORIZATION_HEADER, JwtFilter.BEARER_PREFIX + validToken);

        Claims claims = mock(Claims.class);
        when(claims.get("aud")).thenReturn("1");
        when(claims.getSubject()).thenReturn("username");

        AccessToken accessToken = mock(AccessToken.class);
        when(accessToken.getData()).thenReturn(claims);

        when(tokenService.convertAccessToken(anyString())).thenReturn(accessToken);

        // When
        jwtFilter.doFilterInternal(request, response, filterChain);

        // Then
        verify(tokenService, times(1)).convertAccessToken(validToken);
        verify(tokenService, times(1)).setAuthentication(1L, "username");
        verify(filterChain, times(1)).doFilter(request, response);
    }
    
    // 생략...
}
  1. 정상적인 토큰이 도착한 경우
  2. 이상한 토큰이 도착한 경우
  3. (public 엔드포인트 처럼) 토큰이 없는 요청이 도착한 경우

생략했지만 위 세 가지 시나리오로 테스트 코드를 작성했다.

- Request/Response도 Mocking할 수 있다니

		MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        request.addHeader(JwtFilter.AUTHORIZATION_HEADER, JwtFilter.BEARER_PREFIX + validToken);

MockHttpServletRequestHttpServletRequest의 인터페이스를 구현하고 있어서,
1) 실제 서버/클라이언트 없이도 가짜 요청을 만들 수 있고,
2) addHeader(), setMethod(), setRequestURI() 등의 메소드를 사용할 수 있다.

나의 경우 헤더에 토큰을 넣는 용도로 사용했다.

cf) HttpServletRequest

  • Tomcat이 DispatcherServlet으로 주는 그거..
  • doGet(), doPost()에서 쓰던 그거..

2. JwtExceptionFilter 테스트하기

@ExtendWith(MockitoExtension.class)
class JwtExceptionFilterTest {

    @InjectMocks
    private JwtExceptionFilter jwtExceptionFilter;

    @Mock
    private FilterChain filterChain;

    @Mock
    private HttpServletRequest request;

    @Mock
    private HttpServletResponse response;

    @Mock
    private PrintWriter printWriter;

    @Test
    @DisplayName("ExpiredJwtException 발생 시, 적절한 예외처리 객체 반환")
    void doFilterInternal_WithExpiredJwtException() throws IOException, ServletException {
        // Given
        doThrow(new ExpiredJwtException(null, null, "Token expired"))
                .when(filterChain).doFilter(request, response);
        when(response.getWriter()).thenReturn(printWriter);

        // When
        jwtExceptionFilter.doFilterInternal(request, response, filterChain);

        // Then
        verify(response).setStatus(HttpStatus.UNAUTHORIZED.value());
        verify(response).setContentType("application/json; charset=UTF-8");
        ErrorResponseDto expectedErrorResponse = new ErrorResponseDto(ACCESS_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED);
        verify(printWriter).write(new ObjectMapper().writeValueAsString(expectedErrorResponse));
    }
    
    // 생략...
}

코드는 생략했지만 이번에도

  1. ExpiredJwtException (커스텀 예외) 발생 시
  2. JwtException 발생 시
  3. 위 두 예외를 제외한 다른 예외 발생 시
  4. 예외 발생하지 않은 경우

네 가지 시나리오로 작성했다.

doThrow(new ExpiredJwtException(null, null, "Token expired"))
                .when(filterChain).doFilter(request, response);

이 부분을 잘 보고, 그냥 filterChain.doFilter에서 원하는 예외가 난 상황을 만들어주면 된다.



Filter 코드를 짜면서 계속 든 생각은 'Request도 모킹이 되네?', 'FilterChain도 mocking이 되네?' 하는 것이었는데,

막상 정리하면서 다시 보니 왜 쟤네들은 모킹할 수 없다고 생각했는지 모르겠다는 생각이 든다.
어차피 다 그냥 stub일 뿐인데...

0개의 댓글