JUnit 테스트 - AccountController 테스트

jeongjin-kim·2023년 7월 19일

JUnit5

목록 보기
7/11
post-thumbnail

최주호 강사님의 인프런 강좌 정리 및 실습한 기록, 이전 내용과 이어집니다.

목표

  • Account 를 등록하는 @RestController 를 테스트한다.

구현사항

  • @MockMvc 환경에서 특정 유저가 AccountDB 에 저장 한다.
    - @Test 메서드 실행 전에, DB 에 AppUser 를 1건 집어넣는다.
    - /api/login 에서 Bearer ... 토큰을 가져오지 않고, 시큐리티 세션을 생성해서 로그인을 '한 것' 처럼 넘어가고, DB 를 타는 로직에만 집중한다.
    - @WithUserDetails 어노테이션을 사용한다.

@BeforeEach

해당 어노테이션은 @Test 가 실행될 때 마다 실행된다. 만약, N개 @Test 가 실행되면, 실행되는 것에는 순서가 없다.
다만, @Order(i) 를 사용해서 실행순서를 정할 수 있다.

스프링 시큐리티를 사용한다면, 인증 및 인가를 한 뒤 사용자가 토큰을 가지고 있어야 자원에 접근할 수 있다. 권한이 없다면 JWT 를 검증하는 단계에서 에러가 발생해서 더 이상 진행이 되지 않는다.

그럼 테스트 환경에서는 어떻게 해야할까?

강의에서는 다음과 같이 진행한다.

  1. Dummy 데이터를 1건 repository 에 저장한다.

  1. @WithUserDetails 를 이용해서 로그인이 '된 것' 처럼 진행한다.
    즉, JwtAuthenticationFilter 를 통과하기 위해서 테스트 환경에서 Authentication 를 가지고 있는 시큐리티 세션을 생성한다.

  2. 그렇게 하기 위해서는, 옵션을 지정해줘야 한다. setupBefore = TestExecutionEvent.TEST_EXCECUTION)
    default 로 설정된 setupBefore= TestExecutionEvent.TEST_METHOD)@BeforeEach 처럼 다른 메서드 실행 이전 시점에 이미 실행이 되어버린다. 아직 DB 에 등록된 유저가 없는 시점에서 해당 유저의 인증 정보를 가져오려고 하니 오류가 발생한다.

/**
 * jwt token 이 AuthenticationFilter 를 통과해서
 * SecurityContextHolder 에 저장되며, Security Session 이 생성된다.
 * 해당 세션은 ThreadLocal 에 저장되어 멀티-스레드 safe 하다.
 * <p>
 * {@link WithUserDetails} 은 DB 에서 <em>username=ssar</em> 을 조회해서 시큐리티 세션에 담는다. <br />
 * {@code setupBefore=TEST_METHOD} 은 {@code setUp()} 메서드 실행전에 수행된다.
 * {@code setUp()} 메서드가 실행되기 전에 ssar 을 찾으려고 하니 실패한다. <br />
 * {@code setupBefore=TEST_EXECUTION} 은 {@code save_account_test()} 메서드 실행전에 수행된다.
 */
@Test
@DisplayName("save_account_test")
@WithUserDetails(value = "ssar", setupBefore = TestExecutionEvent.TEST_EXECUTION)
public void save_account_test() throws Exception {

    /* given */
    AccountSaveRequestDTO accountSaveRequestDTO = new AccountSaveRequestDTO();
    accountSaveRequestDTO.setNumber(9999L);
    accountSaveRequestDTO.setPassword(1234L);

    String requestBody = objectMapper.writeValueAsString(accountSaveRequestDTO);
    System.out.println(requestBody);

    /* when */
    ResultActions resultActions = mockMvc
            .perform(post("/api/account/save").content(requestBody).contentType(MediaType.APPLICATION_JSON));
    String responseBody = resultActions.andReturn().getResponse().getContentAsString();
    System.out.println(responseBody);

    /* then */
    resultActions.andExpect(status().isCreated());
}

테스트 결과

고찰

김영한님, 백기선님 강의에서도 마찬가지로 하나의 메서드에 여러가지 역할을 부여하는 것은 좋지 못하다. 관심사를 분리하는 것이 좋다.

어떤 환경을 구성하는 부분, 그리고 구성이 완료된 환경을 주입받아서 사용만 하는 부분 등 decoupling 을 고려해야한다.

Account 을 DB 에 저장하는 테스트도 이렇게 생각했다.

  1. token 을 저장할 전역 변수를 생성한다.
  2. 로그인 후 token 값을 저장하는 메서드를 생성한다.
  3. @Test 를 진행한다.

하지만 강의에서는 빠른 진도를 위해서도 있겠지만, 로그인하는 것 자체가 중요한 것이 아니고 Account 을 DB 에 저장하는 로직을 테스트하는 것이 목적이다.

로그인하고, 토큰 생성 후 JwtAuthenticationFilter 를 통과시키는 것 까지 단 2줄로 해결했다. 당연히 Spring Security 가 지원을 하기에 가능한 일이지만 아무런 생각을 하지 않았음을 반성한다.

import org.springframework.security.test.context.support.TestExecutionEvent;
import org.springframework.security.test.context.support.WithUserDetails;

0개의 댓글