스프링 프레임워크/test

Springboot test - 1 - controller test

blogger903 2024. 8. 7. 09:33
728x90

Test Slicing^1 이란 테스트를 위해 ApplicationContext를 분리하는걸 말합니다
MockMvc를 이용하여 Controller 테스트를 진행할 수 있습니다

다음과 같이 특정 컨트롤러만 테스트할 수 있습니다

@WebMvcTest(FeedControllerV2.class)
public class FeedControllerV2Test {

@WebMvcTest는 해당 컨트롤러만 Bean으로 등록되지않고, ControllerAdvice, Converter, WebMvcConfigurer, HandlerMethodArgumentResolver^2 등이 등록되며 애노테이션 주석으로 확인해볼 수 있습니다

컨트롤러 테스트의 목적은 입력검증, 출력검증입니다.

예시 테스트 코드를 보면서 어떤 것들이 가능한지 확인해보겠습니다.


@WebMvcTest(FeedControllerV2.class)
public class FeedControllerV2Test {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private FeedServiceV2 feedServiceV2;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    @DisplayName("[성공] 피드를 조회합니다")
    void getFeeds_Success() throws Exception {
        FeedDtoV2 feedDto = FeedDtoTestFixtureV2.createSampleFeedDtoV2();
        List<FeedDtoV2> feeds = Collections.singletonList(feedDto);
        FeedSearchRequestV2 request = FeedSearchRequestV2.builder()
                .memberName("yobs")
                .page(1)
                .size(10)
                .build();

        given(feedServiceV2.searchFeeds(any(FeedSearchRequestV2.class))).willReturn(feeds);

        String expectedResponse = objectMapper.writeValueAsString(feeds);

        mockMvc.perform(MockMvcRequestBuilders.get("/feeds/v2")
                        .param("memberName", request.getMemberName())
                        .param("page", String.valueOf(request.getPage()))
                        .param("size", String.valueOf(request.getSize())))
                .andExpect(status().isOk())
                .andExpect(content().json(expectedResponse));
    }

    @Test
    @DisplayName("[성공] 존재하는 id로 feed 조회시 Feed를 조회합니다")
    void getFeedById_Success() throws Exception {

        FeedDtoV2 feedDto = FeedDtoTestFixtureV2.createSampleFeedDtoV2();
        given(feedServiceV2.getFeedById(feedDto.getId())).willReturn(feedDto);

        String expectedFeedDto = objectMapper.writeValueAsString(feedDto);

        mockMvc.perform(MockMvcRequestBuilders.get("/feeds/v2/{id}", 1L))
                .andExpect(status().isOk())
                .andExpect(content().json(expectedFeedDto));

    }

    @Test
    @DisplayName("[실패] Feed가 존재하지 않는 경우 BadRequest를 반환합니다")
    void getFeedById_NotFound() throws Exception {
        given(feedServiceV2.getFeedById(anyLong())).willThrow(new IllegalArgumentException("Feed not found"));

        mockMvc.perform(MockMvcRequestBuilders.get("/feeds/v2/{id}", 1L))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.message").value("Feed not found"));
    }

    @Test
    @DisplayName("[실패] 페이지 번호가 유효하지 않음")
    void getFeeds_InvalidPage() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/feeds/v2")
                        .param("memberName", "yobs")
                        .param("page", "0") // Invalid page number
                        .param("size", "300"))
                .andExpect(status().isBadRequest());
    }

    @Test
    @DisplayName("[실패] 페이지 크기가 유효하지 않음")
    void getFeeds_InvalidSize() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/feeds/v2")
                        .param("memberName", "yobs")
                        .param("page", "1")
                        .param("size", "300")) // Invalid size
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.message").value(String.format("size: %s", SIZE_VALIDATION_MESSAGE)));
    }
}

컨트롤러에 테스트에 적용한 것들

  • MockMvc를 통해 Controller의 입력 파라미터의 유효성 체크 검증 합니다
    • 유효성 검증 테스트를 위해서는 ControllerAdvice를 구현하여 메시지를 비교하여 검증할 수 있습니다
  • ObjectMapper와 MockMvcResultMatche.jsonPath를 통해 출력 검증이 가능합니다.
    • ObjectMapper를 통해 json의 상세한 필드를 일일이 테스트 코드로 작성하여 테스트코드도 유지보수 대상이므로 더 심플한 테스트코드를 유지할 수 있습니다.
  • MockBean을 통해 BDDMockito에서 지원해주는 mocking으로 controller가 가진 의존성을 격리하여 controller 기능에 집중하여 테스트합니다.
  • TestFixture통해 테스트에 필요한 자원을 따로 관리하고 테스트코드 가독성을 높입니다.

참고: https://tecoble.techcourse.co.kr/post/2021-05-18-slice-test/