TestCode

컨트롤러 테스트하기

salmon16 2024. 6. 30. 18:21

컨트롤러

  • 외부 세계의 요청을 가장 먼저 받는 계층
  • 파라미터에 대한 최소한의 검증을 수행하는 것을 목표로 한다.
  • Business Layer, Persistence Layer를 Mocking 해서 테스트를 해보자.

실습

  • ProductController에 대해 TestCode를 작성해 보자
  • @WebMvcTest(controllers = ProductController.class) 어노테이션을 사용해 ProductController와 관련된 빈만 로드하여 최소한의 빈만 로드한다.
  • 가짜 객체인 Mock객체를 주입받기 위해 @MockBean을 이용해 private ProductService를 선언해 준다.
  • MockMvc를 주입받기 위해 @Autowired를 통해 주입받는다
    • MockMvc 객체를 통해 컨트롤러의 요청과 응답을 실제 서블릿 환경처럼 테스트할 수 있다. 이를 통해 HTTP 요청을 만들어 컨트롤러에 보내고, 그 응답을 검증하는 등의 작업을 수행할 수 있다.
 @DisplayName("신규 상품을 등록한다.")
    @Test
    void createProduct() throws Exception {
        // given
        ProductCreateRequest request = ProductCreateRequest.builder()
                .type(ProductType.HANDMADE)
                .sellingStatus(ProductSellingStatus.SELLING)
                .name("아메키라노")
                .price(4000)
                .build();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/products/new")
                .content(objectMapper.writeValueAsString(request))
                .contentType(MediaType.APPLICATION_JSON)
        )
                .andDo(MockMvcResultHandlers.print())
                .andExpect(MockMvcResultMatchers.status().isOk());      
    }

 

  • mockMvc.perform을 통해 http 요청을 보낸다.
  • post요청으로  /api/v1/products/new로 보낸다.
  • body에는 위에서 생성한 request를 보내는데 request객체를 직렬화하기 위해 objectMapper를 사용한다.
  • andDo(MockMvcResultHandlers.print()를 통해 상세한 테스트 과정을 볼 수 있다.
  • andExpect를 통해 test의 결과를 확인한다.

NotNull, Positive, NotBlank 등을 체크해 보자

  • 컨트롤러에서 요청을 받을 때 Dto의 필드값을 NotNull, Positive, NotBlank등을 체크해 보자
implementation 'org.springframework.boot:spring-boot-starter-validation'

위와 같은 validation을 의존성으로 추가한다.

 

@NotNull(message = "상품 타입은 필수입니다.")
private ProductType type;
@NotNull(message = "상품 판매상태는 필수입니다.")
private ProductSellingStatus sellingStatus;
@NotBlank(message = "상품 이름은 필수입니다.")
private String name;
@Positive(message = "상품 가격은 양수여야 합니다.")
private int price;

Dto클래스에 위와 같은 어노테이션으로 validation을 진행하도록 한다.

 

컨트롤러에 @Valid 어노테이션을 사용해 유효성 검사를 진행하도록 한다.

@PostMapping("/api/v1/products/new")
public ApiResponse<ProductResponse> createProduct(@Valid @RequestBody ProductCreateRequest request) {
    return ApiResponse.ok(productService.createProduct(request));
}
@RestControllerAdvice
public class ApiControllerAdvice {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public ApiResponse<Object> bindException(BindException e) {
        return ApiResponse.of(HttpStatus.BAD_REQUEST,
                e.getBindingResult().getAllErrors().get(0).getDefaultMessage(),
                null);
    }
}

Adivce 컨트롤러를 설정을 해 BindException가 발생했을 때 처리하도록 설정한다.

 

@DisplayName("신규 상품을 등록할 때 상품 타입은 필수값이다..")
@Test
void createProductWithOutType() throws Exception {
    // given
    ProductCreateRequest request = ProductCreateRequest.builder()
            .sellingStatus(ProductSellingStatus.SELLING)
            .name("아메키라노")
            .price(4000)
            .build();

    mockMvc.perform(post("/api/v1/products/new")
                    .content(objectMapper.writeValueAsString(request))
                    .contentType(MediaType.APPLICATION_JSON)
            )
            .andDo(print())
            .andExpect(status().isBadRequest())
            .andExpect(jsonPath("$.code").value("400"))
            .andExpect(jsonPath("$.status").value("BAD_REQUEST"))
            .andExpect(jsonPath("$.message").value("상품 타입은 필수입니다."))
            .andExpect(jsonPath("$.data").isEmpty());
}

위 코드와 같이 type이 없는 경우 테스트를 진행해 보자 

  • andExpect를 사용해서 error code를 체크한다.
  • jsonPath를 사용해서 응답받은 json의 값을 체크할 수 있다.

 

'TestCode' 카테고리의 다른 글

JUnit 객체의 필드 값 확인하기  (0) 2024.07.15
Mockito 사용하기  (0) 2024.07.13
Intellij에서 HTTP 요청 보내기  (0) 2024.06.22
@AfterEach  (0) 2024.06.22
스프링부트 실행 시 .sql로 데이터 insert하기  (0) 2024.06.19