컨트롤러
- 외부 세계의 요청을 가장 먼저 받는 계층
- 파라미터에 대한 최소한의 검증을 수행하는 것을 목표로 한다.
- 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 |