test: 기존 Junit 테스트와 동일한 Kotest 기반 테스트 추가

This commit is contained in:
이상진 2025-07-08 10:20:50 +09:00
parent 3ba9c17169
commit 9cfbbf1902
3 changed files with 97 additions and 1 deletions

View File

@ -12,7 +12,10 @@
git clone https://gitea.pricelees.me/pricelees/kopring-validation.git
// 테스트 실행 후 콘솔 출력 결과 확인
src.test/kotlin/com/sangdol/validation/ValidationTest.kt
src/test/kotlin/com/sangdol/validation/DemoControllerTest
// Kotest 버전
src/test/kotlin/com/sangdol/validation/DemoControllerKTest
```
`src/main/kotlin/com/sangdol/validation/DemoDTO.kt` 를 Decompile 하여 상황에 따라 어노테이션이 어떻게 붙는지 확인할 수 있습니다.
- 같은 @NotNull 이라도 jakarta / jetbrains 패키지인지에 따라 처리되는 방법이 다르며, 자세한 내용은 위 글에서 확인할 수 있습니다.

View File

@ -31,6 +31,9 @@ dependencies {
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("io.github.oshai:kotlin-logging-jvm:7.0.3")
testImplementation("io.mockk:mockk:1.14.4")
testImplementation("io.kotest:kotest-runner-junit5:5.9.1")
testImplementation("io.kotest.extensions:kotest-extensions-spring:1.3.0")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

View File

@ -0,0 +1,90 @@
package com.sangdol.validation
import io.kotest.core.annotation.DisplayName
import io.kotest.core.spec.style.BehaviorSpec
import org.hamcrest.CoreMatchers.containsString
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.*
import kotlin.random.Random
@WebMvcTest(controllers = [DemoController::class])
@DisplayName("타입별 Bean Validation 테스트")
class DemoControllerKTest(
@Autowired private val mockMvc: MockMvc,
@MockitoBean private val demoService: DemoService
) : BehaviorSpec() {
init {
Given("Wrapper 타입에서는") {
val endpoint = "/wrapper"
val value = "value"
When("값이 입력되지 않으면") {
val expectedStatus = HttpStatus.BAD_REQUEST.value()
val expectedError = "HttpMessageNotReadableException"
Then("HttpMessageNotReadableException이 발생한다.") {
val body = "{\"withAnnotation\": \"$value\"}"
runException(endpoint, body, expectedStatus, expectedError)
}
Then("@NotNull이 적용되지 않고 HttpMessageNotReadableException이 발생한다.") {
val body = "{\"withoutAnnotation\": \"$value\"}"
runException(endpoint, body, expectedStatus, expectedError)
}
}
}
Given("Primitive 타입에서는") {
val endpoint = "/primitive"
val value = Random.nextInt()
When("값을 입력하지 않아도") {
val expectedStatus = HttpStatus.OK.value()
Then("@NotNull과 무관하게 기본값이 들어간다.") {
run(endpoint, "{\"withoutAnnotation\": \"$value\"}", expectedStatus)
}
}
}
Given("Primitive 타입에서 @NotNull + Nullable 으로 선언한 경우에는") {
val endpoint = "/solution"
When("값이 입력되지 않으면") {
val body = "{\"notExistField\": 10}"
val expectedStatus = HttpStatus.BAD_REQUEST.value()
val expectedError = "MethodArgumentNotValidException"
Then("@NotNull Bean Validation이 적용된다.") {
runException(endpoint, body, expectedStatus, expectedError)
}
}
}
}
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)))
}