package com.sangdol.validation import org.hamcrest.CoreMatchers.containsString import org.junit.jupiter.api.DisplayName import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.test.context.bean.override.mockito.MockitoBean import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.ResultActions import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status @WebMvcTest(controllers = [DemoController::class]) @MockitoBean(types = [DemoService::class]) class DemoControllerTest { @Autowired private val mockMvc: MockMvc? = null @DisplayName("Wrapper 타입인 경우 값이 입력되지 않으면 HttpMessageNotReadableException이 발생한다.") @ParameterizedTest(name = "{0}") @CsvSource( value = [ "@NotNull 필드의 값만 입력되는 경우 ; withAnnotation", "@NotNull이 아닌 필드의 값만 입력되는 경우 ; withoutAnnotation" ], delimiter = ';' ) fun except_wrapper_field(testName: String, field: String) { val endpoint = "/wrapper" val value = "message" val body = "{\"$field\": \"$value\"}" runException(endpoint, body, HttpStatus.BAD_REQUEST.value(), "HttpMessageNotReadableException") } @DisplayName("Primitive 타입인 경우 @NotNull 지정과 무관하게 값이 입력되지 않아도 기본값이 들어간다.") @ParameterizedTest(name = "{0}") @CsvSource( value = [ "@NotNull 필드의 값만 입력되는 경우 ; withAnnotation", "@NotNull이 아닌 필드의 값만 입력되는 경우 ; withoutAnnotation" ], delimiter = ';' ) fun except_primitive_field(testName: String, field: String) { val endpoint = "/primitive" val value = 10 val body = "{\"$field\": $value}" run(endpoint, body, HttpStatus.OK.value()) } @DisplayName("Primitive 타입인 경우 Bean Validation을 위해서는 @NotNull + nullable 필드 지정이 필요하다.") @ParameterizedTest(name = "{0}") @CsvSource( value = [ """필드가 없는 경우 ; {"notExistField": 10} ; 400""", """필드가 Null인 경우 ; {"value": null} ; 400""", """필드의 값이 정상인 경우 ; {"value": 10} ; 200""" ], delimiter = ';' ) fun primitive_validation_with_nullable_field(testName: String, body: String, expectedStatus: Int) { val endpoint = "/solution" if (expectedStatus == HttpStatus.OK.value()) { run(endpoint, body, expectedStatus) } else { runException(endpoint, body, expectedStatus, "MethodArgumentNotValidException") } } private fun run(endpoint: String, body: String, expectedStatus: Int): ResultActions = mockMvc!! .perform( post(endpoint) .contentType(MediaType.APPLICATION_JSON_VALUE) .content(body) ) .andExpect(status().`is`(expectedStatus)) .andDo(print()) private fun runException( endpoint: String, body: String, expectedStatus: Int, expectedExceptionType: String ): ResultActions = run(endpoint, body, expectedStatus) .andExpect( jsonPath( "$.type", containsString(expectedExceptionType) ) ) }