단위테스트 ‐ Controller 계층 - f-lab-edu/jshop GitHub Wiki
컨트롤러 계층은 사용자의 데이터를 서비스 계층으로 넘기는 역할을 한다.
이 계층에서 검사할 것은 전달된 데이터에 대한 검증, Service 계층으로 넘기는 데이터에 대한 검증이다.
여기서도 마찬가지로, Controller 의 동작에 대한 단위테스트이므로, Service 계층은 목 객체를 만들어 진행한다.
Controller
는 사용자로부터 입력을 받는 과정이 필요하다.
하지만 사용자의 입력을 받기 위해서는 스프링 프레임워크에 내장된 톰캣 서버를 실행시켜야 하고 이러한 과정이 추가되면 테스트에 상당한 시간이 소요되게 된다.
MockMvc
는 서버를 띄우지 않고도 요청을 테스트할 수 있는 도구다. HTTP 요청을 모킹하고, 컨트롤러의 응답을 모킹해 이를 검증할 수 있다.
예제코드
@ExtendWith(MockitoExtension.class)
class UserControllerTest {
@InjectMocks
private UserController userController;
@Mock
private UserService userService;
@Captor
private ArgumentCaptor<JoinDto> joinDtoCapture;
private MockMvc mockMvc;
@BeforeEach
public void beforeEach() {
mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
}
@Test
public void 회원가입() throws Exception {
// given
String username = "test";
String email = "[email protected]";
String password = "test";
UserType userType = UserType.USER;
JSONObject requestBody = new JSONObject();
requestBody.put("username", username);
requestBody.put("email", email);
requestBody.put("password", password);
requestBody.put("userType", "USER");
JoinDto joinDto = DtoBuilder.getJoinDto(username, email, password, userType);
// when
ResultActions perform = mockMvc.perform(
MockMvcRequestBuilders.post("/api/join")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody.toString()));
// then
verify(userService, times(1)).joinUser(joinDtoCapture.capture());
JoinDto capturedJoinDto = joinDtoCapture.getValue();
perform.andExpect(MockMvcResultMatchers.status().isNoContent());
assertThat(capturedJoinDto).isEqualTo(joinDto);
}
}
Controller
에서 검증할 내용은 사용자의 RequestBody
로 넘어온 JSON
문자열을 파싱해 DTO로 만들고 이 DTO를 Service
계층으로 넘기는 과정이다.
예제 코드에서 알 수 있듯, userService
를 목으로 만들고, 필요할때 동작을 정의해 사용한다.
userService.joinUser
의 호출 회수를 검증하고, 인수를 캡쳐해 검증을 시도한다.
또한 사용자의 요청과, 그에대한 응답을 테스트하기 위해 MockMvc
를 사용해 요청과 응답을 테스트한다.
기본 MockMvc만 사용하기에는 시큐리티 기능을 사용할 수 없는 문제가 있었다.
이 문제 해결은 https://github.com/f-lab-edu/jshop/wiki/%5B트러블슈팅%5D-@Controller-레이어-테스트-SecurityContext