JUnit5 & Mockito를 이용한 Spring Controller 단위테스트 예시

회원가입/ 유저목록 조회 API

@RestController
@RequiredArgsConstructor
public class UserController {

  private final UserService userService;

  @PostMapping("/users/signUp")
  public ResponseEntity<UserResponse> signUp(@RequestBody SignUpRequest request) {
      return ResponseEntity.status(HttpStatus.CREATED)
          .body(userService.signUp(request));
  }

  @GetMapping("/users")
  public ResponseEntity<List<UserResponse>> findAll() {
      return ResponseEntity.ok(userService.findAll());
  }
}


단위테스트 준비

@ExtendWith(MockitoExtension.class)  // JUniit5와 Mockito를 연동
class UserControllerTest {

  @InjectMocks  // 가짜 객체 주입
  private UserController userController;

  @Mock  // 가짜 Mock 객체 생성
  private UserService userService;

  private MockMvc mockMvc;  //  Spring에서 제공하는 MockMVC를 이용하여 HTTP 호출

  @BeforeEach
  public void init() {
      mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
  }

}


회원가입 단위테스트 메소드

@DisplayName("회원 가입 성공")
@Test
void signUpSuccess() throws Exception {
  // given
  SignUpRequest request = signUpRequest();
  UserResponse response = userResponse();

  doReturn(response).when(userService)
      .signUp(any(SignUpRequest.class));  // any(특정클래스타입) : 특정클래스타입이라면 어떠한 객체도 처리할 수 있음

  // when
  ResultActions resultActions = mockMvc.perform(  // 요청정보 작성
      MockMvcRequestBuilders.post("/users/signUp")  // 요청정보에는 MockMvcRequestBuilders 사용 (요청메소드종류, 내용, 파라미터 등 설정가능)
              .contentType(MediaType.APPLICATION_JSON) 
              .content(new Gson().toJson(request)) 
              // 보내는 데이터는 객체가 아닌 문자열이여야 하므로 별도의 변환 필요하여 Gson 사용해 변환
  );

  // then (검증) : 회원가입 API 호출 결과로 200 response와 응답결과 검증해야함
  MvcResult mvcResult = resultActions.andExpect(status().isOk())
      .andExpect(jsonPath("email", response.getEmail()).exists())  // jsonPath를 이용해 해당 json값이 존재하는지 확인
      .andExpect(jsonPath("pw", response.getPw()).exists())
      .andExpect(jsonPath("role", response.getRole()).exists())
}

//SignUp에 대한 stub
private SignUpRequest signUpRequest() {
    return SignUpRequest.builder()
        .email("test@test.test")
        .pw("test")
        .build();
}

private UserResponse userResponse() {
    return UserResponse.builder()
        .email("test@test.test")
        .pw("test")
        .role(UserRole.ROLE_USER)
        .build();
}


유저목록 조회 단위테스트 메소드

@DisplayName("사용자 목록 조회")
@Test
void getUserList() throws Exception {
  // given
  doReturn(userList()).when(userService)
      .findAll();

  // when
  ResultActions resultActions = mockMvc.perform(
      MockMvcRequestBuilders.get("/user/list")
  );

  // then
  MvcResult mvcResult = resultActions.andExpect(status().isOk()).andReturn();
  
  UserListResponseDTO response = new Gson().fromJson(mvcResult.getResponse()
      .getContentAsString(), UserListResponseDTO.class);  // json 응답을 객체로 변환하여 확인해봄
  assertThat(response.getUserList().size()).isEqualTo(5);
}

//UserService의 findAll에 대한 Stub
private List<UserResponse> userList() {
  List<UserResponse> userList = new ArrayList<>();
  for (int i = 0; i < 5; i++) {
      userList.add(new UserResponse("test@test.test", "test", UserRole.ROLE_USER));
  }
  return userList;
}


@WebMvcTest

SpringBoot가 제공하는 Controller Test annotation

  • MockMvc 객체 자동생성
  • 웹계층 테스트에 필요한 요소들을 모두 빈으로 등록해 스프링컨텍스트 환경 구성
    • ControllerAdvice
    • Filter
    • Interceptor 등
  • @Mock 대신 @MockBean
  • @Spy 대신 @SpyBean


Reference
https://mangkyu.tistory.com/145

업데이트: