트러블 슈팅

MockMvc 테스트 코드 작성중 문제

nameless1004 2024. 9. 10. 00:09

문제

@RestController
@RequiredArgsConstructor
public class CommentController {

    private final CommentService commentService;

    @PostMapping("/todos/{todoId}/comments")
    public ResponseEntity<CommentSaveResponse> saveComment(
            @Auth AuthUser authUser,
            @PathVariable("todoId") long todoId,
            @Valid @RequestBody CommentSaveRequest commentSaveRequest
    ) {
        return ResponseEntity.ok(commentService.saveComment(authUser, todoId, commentSaveRequest));
    }

    @GetMapping("/todos/{todoId}/comments")
    public ResponseEntity<List<CommentResponse>> getComments(@PathVariable long todoId) {
        return ResponseEntity.ok(commentService.getComments(todoId));
    }
}

이 클래스에 대한 테스트 코드를 작성하다가 문제가 생겼다.

첫번째는 @Auth라는 아규먼트리졸버

두번째는 테스트를 작성했는데 status는 200이 잘 나오는데 body에 아무것도 나오지 않았다.

해결

여러 정보들을 찾아봐도.. 해결하는 방법이 딱 나오지 않아서 이리저리 치였다.

먼저 첫 번째 문제인 @Auth라는 아규먼트 리졸버 부분을 해결했다.

    @BeforeEach
    public void setup(){
        mockMvc = MockMvcBuilders.standaloneSetup(controller)
            .setCustomArgumentResolvers(resolver)
            .build();
    }

standaloneSetup으로 해준 후 커스텀아규먼트 리졸버에 AuthUserArgumentResolver를 Mock으로 만들고 등록했다.

@WebMvcTest(controllers = {CommentController.class})
class CommentControllerTest {

    @MockBean
    private CommentService commentService;

    @Mock
    private AuthUserArgumentResolver resolver;

    @MockBean
    private CommentController controller;

    private MockMvc mockMvc;

    @BeforeEach
    public void setup(){
        mockMvc = MockMvcBuilders.standaloneSetup(controller)
            .setCustomArgumentResolvers(resolver)
            .build();
    }

    @Test
    public void test() throws Exception {
        // given
        given(resolver.supportsParameter(any())).willReturn(true);
        given(resolver.resolveArgument(any(), any(), any(), any())).willReturn(
            new AuthUser(1L, "email", UserRole.USER));
        CommentSaveRequest saveRequest = new CommentSaveRequest("test");
        ObjectMapper mapper = new ObjectMapper();
        String s = mapper.writeValueAsString(saveRequest);

        // when
        CommentSaveResponse resonse = new CommentSaveResponse(1L, "test",
            new UserResponse(1L, "email"));

        when(commentService.saveComment(any(AuthUser.class), anyLong(),
            any(CommentSaveRequest.class))).thenReturn(
            new CommentSaveResponse(1L, "test", new UserResponse(1L, "email")));

        mockMvc.perform(post("/todos/{todoId}/comments", 1)
                .content(s)
                .contentType(MediaType.APPLICATION_JSON)).andDo(print())
            .andExpect(status().is(HttpStatus.OK.value()));
        // then
    }
}

테스트 코드는 초록불이 뜨고, status도 200이 뜨는데 Response의 Body에 아무것도 안나오는 것을 볼 수 있었다.

이 문제도 테스트 코드를 너무 안짜봤던 경험이 문제였던 것 같다. 현재 코드에서는 controller 이부분이 MockBean으로 되어있는데 Autowired로 주입받아와서 해결했다.

@WebMvcTest(controllers = {CommentController.class})
class CommentControllerTest {

    @MockBean
    private CommentService commentService;

    @Mock
    private AuthUserArgumentResolver resolver;

    @Autowired
    private CommentController controller;

    private MockMvc mockMvc;

    @BeforeEach
    public void setup(){
        mockMvc = MockMvcBuilders.standaloneSetup(controller)
            .setControllerAdvice(new GlobalExceptionHandler())
            .setCustomArgumentResolvers(resolver)
            .build();
    }

    @Test
    public void test() throws Exception {
        // given
        given(resolver.supportsParameter(any())).willReturn(true);
        given(resolver.resolveArgument(any(), any(), any(), any())).willReturn(
            new AuthUser(1L, "email", UserRole.USER));
        CommentSaveRequest saveRequest = new CommentSaveRequest("test");
        ObjectMapper mapper = new ObjectMapper();
        String s = mapper.writeValueAsString(saveRequest);
        CommentSaveResponse resonse = new CommentSaveResponse(1L, "test",
            new UserResponse(1L, "email"));

        when(commentService.saveComment(any(AuthUser.class), anyLong(),
            any(CommentSaveRequest.class))).thenReturn(
            new CommentSaveResponse(1L, "test", new UserResponse(1L, "email")));

        // when / then
        mockMvc.perform(post("/todos/{todoId}/comments", 1)
                .content(s)
                .contentType(MediaType.APPLICATION_JSON)).andDo(print())
            .andExpect(status().is(HttpStatus.OK.value()));
    }
}

잘 나오는 것을 볼 수 있다.

글은 짧게 있지만.. 5시간 동안 헤맸다. 튜터님께 여쭤봤는데 내일 강의에서 알려주신다고 하셨다. 이 문제를 꼭 해결해보고 싶어서 이곳저곳 뒤적거리고 생각해보면서 잘 해결해서 뿌듯하다. 다만, Mockito를 이용해서 테스트 코드를 작성하는게 이번이 처음이라 잘못된 방법일 수도 있어서, 내일 한 번 확인해보고 다시 수정해봐야할 것 같다.