๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Spring

Spring Boot + MockMvc ํ…Œ์ŠคํŠธ(feat. Kotlin)

by ์ฃผ๋ฐœ2 2021. 9. 7.
๋ฐ˜์‘ํ˜•

๐Ÿ“Ž  Spring Boot + MockMvc ํ…Œ์ŠคํŠธ

์•ˆ๋…•ํ•˜์„ธ์š”, ์ด๋ฒˆ ์‹œ๊ฐ„์— ์ •๋ฆฌํ•  ๋‚ด์šฉ์€ ์Šคํ”„๋ง ๋ถ€ํŠธ์™€ MockMvc๋ฅผ ํ†ตํ•œ GET, POST ๋“ฑ์˜ API๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๋ฒ•์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ „์ฒด ์ฝ”๋“œ๋Š” ๊นƒํ—ˆ๋ธŒ์—์„œ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค :)

โ€ป ํฌ์ŠคํŒ…์— ํ‹€๋ฆฐ ๋‚ด์šฉ์ด ์กด์žฌํ•˜๋ฉด ์ง€์  ๋ถ€ํƒ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค!

 

 

 

โ˜… MockMvc๋ž€?


MockMvc๋Š” Spring MVC์˜ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ๊ฐ€์งœ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„œ๋ฒ„์— ๋ฐฐํฌํ•˜์ง€ ์•Š๊ณ ๋„ Spring MVC์˜ ๋™์ž‘์„ ์žฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.

์ด๋ฅผ ํ†ตํ•ด ๊ฐ€์งœ์˜ HTTP ์š”์ฒญ์„ ์ปจํŠธ๋กค๋Ÿฌ์— ๋ณด๋‚ด๊ณ  ์„œ๋ฒ„ ๋‚ด์—์„œ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋™์ž‘์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

MockMvc๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ์„œ ์‹ค์ œ ์„œ๋ฒ„ ํ™˜๊ฒฝ๊ณผ ๋™์ผํ•œ @SpringBootTest ์–ด๋…ธํ…Œ์ด์…˜์ด ์•„๋‹Œ @WebMvcTest ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ์ˆœ์ˆ˜ Controller ๋ ˆ์ด์–ด์˜ ๋กœ์ง์„ ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

โ˜… MockMvc ์‚ฌ์šฉ ์˜ˆ์‹œ - ์˜์กด์„ฑ ์ถ”๊ฐ€ & ๊ฐ์ฒด ์ฝ”๋“œ ์ž‘์„ฑ


์˜ˆ์ œ ์ฝ”๋“œ๋Š” Kotlin ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ์„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

1) ๋จผ์ € ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

// gradle
testImplementation("org.springframework.boot:spring-boot-starter-test")

// maven
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>2.5.4</version>
    <scope>test</scope>
</dependency>

 

 

 

2) ํ…Œ์ŠคํŠธ์— ์‚ฌ์šฉ๋  ErrorResponse, UserRequest ํด๋ž˜์Šค์˜ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

package org.juhyun.kotlinspringboot.model.exception

import com.fasterxml.jackson.annotation.JsonProperty
import java.time.LocalDateTime


/* ErrorResponse API
{
    "result_code": "FAIL",
    "http_status": "400",
    "http_method": "GET",
    "message": "์š”์ฒญ์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.",
    "path": "/api/exception",
    "timestamp": "2021-09-06T16:03:43.556134",
    "errors":[
        {
        "field": "name",
        "message": "ํฌ๊ธฐ๊ฐ€ 2์—์„œ 6 ์‚ฌ์ด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค",
        "value": "LeeJuHyun"
        }
    ]
}
*/
data class ErrorResponse(

        @field: JsonProperty("result_code")
        var resultCode: String? = null,

        @field: JsonProperty("http_status")
        var httpStatus: String? = null,

        @field: JsonProperty("http_method")
        var httpMethod: String? = null,
        var message: String? = null,
        var path: String? = null,
        var timestamp: LocalDateTime? = null,
        var errors: MutableList<Error>? = mutableListOf()
)

data class Error(
        var field: String? = null,
        var message: String? = null,
        var value: Any? = null
)


package org.juhyun.kotlinspringboot.model

import org.juhyun.kotlinspringboot.annotation.StringFormatDateTime
import java.lang.Exception
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import javax.validation.constraints.*
import kotlin.math.min

data class UserRequest(

        @field: NotEmpty
        @field: Size(min = 2, max = 10)
        var name: String? = null,

        @field: PositiveOrZero // 0๋ณด๋‹ค ํฐ ์–‘์ˆ˜
        var age: Int? = null,

        @field: Email
        var email: String? = null,

        @field: NotBlank
        var address: String? = null,

        @field: Pattern(regexp = "\\d{3}-\\d{3,4}-\\d{4}\$")
        var phoneNumber: String? = null,

        @field:StringFormatDateTime(pattern = "yyyy-MM-dd HH:mm:ss", message = "ํŒจํ„ด์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
        var createdAt: String? = null // yyyy-MM-dd HH:mm:ss
)

ErrorResponse ํด๋ž˜์Šค์—๋Š” ์Šค๋„ค์ดํฌ ์ผ€์ด์Šค๋กœ ์‘๋‹ตํ•˜๊ธฐ ์œ„ํ•ด @JsonProperty ์–ด๋…ธํ…Œ์ด์…˜์„ ์ ์šฉํ•ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

UserRequest ํด๋ž˜์Šค์—๋Š” ๊ฐ ํ•„๋“œ์—๋Œ€ํ•œ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

 

 

 

โ˜… MockMvc ์‚ฌ์šฉ ์˜ˆ์‹œ - HTTP GET ํ…Œ์ŠคํŠธ


3) HTTP GET ํ…Œ์ŠคํŠธ

HTTP GET API ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด Controller ๋ฐ Test๋ฅผ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

@RestController
@RequestMapping("/api/exception")
@Validated
class ExceptionApiController {

    @GetMapping("/hello")
    fun hello(): String {
        val list = mutableListOf<String?>()
        return "Hello"
    }

    @GetMapping
    fun get(
            @NotBlank @Size(min = 2, max = 6) @RequestParam name: String,
            @Min(10) @RequestParam age: Int): String {
        println(name)
        println(age)
        return "$name $age"
    }
    
    ...
    
}

 

  • hello() ๋ฉ”์†Œ๋“œ: ๋‹จ์ˆœ ๋ฌธ์ž์—ด("Hello") ๋ฆฌํ„ด
  • get() ๋ฉ”์†Œ๋“œ: name, age ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›์œผ๋ฉฐ ์œ ํšจ์„ฑ ๊ฒ€์ฆ 

 

 

@WebMvcTest
@AutoConfigureMockMvc
class ExceptionApiControllerTest {

    @Autowired
    private lateinit var mockMvc: MockMvc

    @Test
    fun helloTest() {
        val uri: String = "/api/exception/hello"
        mockMvc.perform(MockMvcRequestBuilders.get(uri))
                .andExpect(MockMvcResultMatchers.status().isOk)
                .andExpect(MockMvcResultMatchers.content().string("Hello"))
                .andDo(MockMvcResultHandlers.print())
    }

    @Test
    fun getTest() {
        val uri = "/api/exception"
        val queryParams = LinkedMultiValueMap<String, String>()
        queryParams.add("name", "JuHyun")
        queryParams.add("age", "20")

        mockMvc.perform(MockMvcRequestBuilders.get(uri).queryParams(queryParams))
                .andExpect(MockMvcResultMatchers.status().isOk)
                .andExpect(MockMvcResultMatchers.content().string("JuHyun 20"))
                .andDo(MockMvcResultHandlers.print())
    }

    @Test
    fun ์ด๋ฆ„์€_2๊ธ€์ž์—์„œ_6๊ธ€์ž_์‚ฌ์ด์—ฌ์•ผํ•œ๋‹ค() {
        val uri = "/api/exception"
        val queryParams = LinkedMultiValueMap<String, String>()
        queryParams.add("name", "JuHyunJuHyun")
        queryParams.add("age", "11")

        mockMvc.perform(MockMvcRequestBuilders.get(uri).queryParams(queryParams))
                .andExpect(MockMvcResultMatchers.status().isBadRequest)
                .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
                .andDo(MockMvcResultHandlers.print())
    }

    @Test
    fun ๋‚˜์ด๋Š”_10๋ณด๋‹ค_์ปค์•ผํ•œ๋‹ค() {
        var uri = "/api/exception"
        val queryParams = LinkedMultiValueMap<String, String>()
        queryParams.add("name", "JuHyun")
        queryParams.add("age", "9")

        mockMvc.perform(MockMvcRequestBuilders.get(uri).queryParams(queryParams))
                .andExpect(MockMvcResultMatchers.status().isBadRequest)
                .andExpect(MockMvcResultMatchers.jsonPath("\$.result_code").value("FAIL"))
                .andExpect(MockMvcResultMatchers.jsonPath("\$.errors[0].field").value("age"))
                .andExpect(MockMvcResultMatchers.jsonPath("\$.errors[0].value").value("9"))
                .andDo(MockMvcResultHandlers.print())
    }

 

perform()

  • mock ๊ฐ์ฒด์˜ ์š”์ฒญ์„ ์ „์†กํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋ฉฐ ๊ฒฐ๊ณผ๋กœ ResultActions ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  • ResultActions ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋ฆฌํ„ด ๊ฐ’์„ ๊ฒ€์ฆํ•˜๊ณ  ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” andExpect(), ์ˆ˜ํ–‰ํ•˜๋Š” andDo(), ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” andReturn() ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” RequestBuilder ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ฐ›์œผ๋ฉฐ ์ด๋ฅผ ์œ„ํ•ด ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์†Œ๋“œ์ธ MockMvcRequestBuilders ์ถ”์ƒ ํด๋ž˜์Šค๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

RequestBuilders๋ฅผ ์ƒ์† & ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค, ํด๋ž˜์Šค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • SmartRequestBuilder
  • ConfigurableSmartRequestBuilder
  • MockHttpServletRequestBuilder
  • ...

์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” MockMvcRequestBuilders ํด๋ž˜์Šค์˜ ๋ฉ”์†Œ๋“œ๋Š” MockHttpServletRequestBuilder ํด๋ž˜์Šค๋ฅผ 

๋ฆฌํ„ดํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด perform() ๋ฉ”์†Œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ MockMvcRequestBuilders ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

andExpect()

  • perform() ์š”์ฒญ์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.
  • ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ResultMatcher ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ฐ›์œผ๋ฉฐ ์ด๋ฅผ ์œ„ํ•ด ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์†Œ๋“œ์ธ MockMvcResultMatchers ์ถ”์ƒ ํด๋ž˜์Šค๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

andDo()

  • ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ํŠน์ • ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ResultHandler ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ฐ›์œผ๋ฉฐ ์ด๋ฅผ ์œ„ํ•ด ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์†Œ๋“œ์ธ MockMvcResultHandlers ์ถ”์ƒ ํด๋ž˜์Šค๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

get()

  • ๊ฒฝ๋กœ์— ํ•ด๋‹นํ•˜๋Š” String ํ˜น์€ URI๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.

queryParams()

  • ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ํ•„์š”ํ•œ ์š”์ฒญ์˜ ๊ฒฝ์šฐ ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ MultiValueMap<>์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  • ๋น„์Šทํ•œ ๋ฉ”์†Œ๋“œ๋กœ param(), params(), queryParam() ๋“ฑ์˜ ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

jsonPath()

  • Response Body์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์†Œ๋“œ ์ž…๋‹ˆ๋‹ค.

์œ„ ์‚ฌ์ง„์€ ์ด๋ฆ„์€_2๊ธ€์ž์—์„œ_6๊ธ€์ž_์‚ฌ์ด์—ฌ์•ผํ•œ๋‹ค() ๋ฉ”์†Œ๋“œ์˜ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ์ž…๋‹ˆ๋‹ค.

 

 

 

โ˜… MockMvc ์‚ฌ์šฉ ์˜ˆ์‹œ - HTTP POST ํ…Œ์ŠคํŠธ


4) HTTP-POST ํ…Œ์ŠคํŠธ

HTTP POST API๋ฅผ ํ…Œ์ŠคํŠธํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@RestController
@RequestMapping("/api/exception")
@Validated
class ExceptionApiController {

    ...

    @PostMapping
    fun post(@Valid @RequestBody userRequest: UserRequest): UserRequest {
        println(userRequest)
        return userRequest
    }

    @ExceptionHandler(ConstraintViolationException::class)
    fun constraintViolationException(e: ConstraintViolationException, request: HttpServletRequest): ResponseEntity<ErrorResponse>? {
        // 1. ์—๋Ÿฌ ๋ถ„์„
        val errors = mutableListOf<Error>()
        e.constraintViolations.forEach {
            val error = Error().apply {
                this.field = it.propertyPath.last().name
                this.message = it.message
                this.value = it.invalidValue
            }
            errors.add(error)
        }

        // 2. ErrorResponse
        val errorResponse = getErrorResponse(request, errors)

        // ResponseEntity
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse)
    }

    private fun getErrorResponse(request: HttpServletRequest, errors: MutableList<Error>): ErrorResponse {
        return ErrorResponse().apply {
            this.resultCode = "FAIL"
            this.httpStatus = HttpStatus.BAD_REQUEST.value().toString()
            this.httpMethod = request.method
            this.message = "์š”์ฒญ์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค."
            this.path = request.requestURI.toString()
            this.timestamp = LocalDateTime.now()
            this.errors = errors
        }
    }

Controller์—์„œ post ๋ฉ”์†Œ๋“œ๋Š” UserRequest ๊ฐ์ฒด๋งŒ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•„ ์ถœ๋ ฅํ•œ ํ›„ ๋ฆฌํ„ดํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค.

 

 

@WebMvcTest // ๊ธ€๋กœ๋ฒŒ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š” ํฌํ•จ X
@AutoConfigureMockMvc
class ExceptionApiControllerTest {

    @Test
    fun postTest() {
        val userRequest = UserRequest().apply{
            this.name = "JuHyun"
            this.age = 20
            this.phoneNumber = "010-1111-2222"
            this.address = "์„œ์šธ"
            this.email = "a@a.com"
            this.createdAt = "2021-09-06 21:11:12"
        }

        // JSON ํ˜•์‹์˜ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
        val json = jacksonObjectMapper().writeValueAsString(userRequest)
        println(json)

        val uri = "/api/exception"

        mockMvc.perform(
                MockMvcRequestBuilders.post(uri)
                        .content(json)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isOk)
                .andExpect(MockMvcResultMatchers.jsonPath("\$.name").value("JuHyun"))
                .andExpect(MockMvcResultMatchers.jsonPath("\$.age").value("20"))
                .andExpect(MockMvcResultMatchers.jsonPath("\$.address").value("์„œ์šธ"))
                .andDo(MockMvcResultHandlers.print())
    }

val json = jacksonObjectMapper().writeValueAsString(userRequest)

  • ์œ„ ๋ฉ”์†Œ๋“œ๋Š” ๋ฌธ์ž์—ด์„ JSON ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค.
  • println(json) ์˜ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

content()

  • Request Body์— ๋ฌธ์ž์—ด์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

 

postTest() ๋ฉ”์†Œ๋“œ์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค.

 

์œ„ ์˜ˆ์ œ์—์„œ๋Š” ์ž‘์„ฑํ•˜์ง€ ์•Š์•˜์ง€๋งŒ MockMvcRequestBuilders, MockMVcResultMatchers ํด๋ž˜์Šค์˜ ๋ชจ๋“  ๋ฉ”์†Œ๋“œ๋“ค์€ ์ „๋ถ€ ์ •์  ๋ฉ”์†Œ๋“œ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํด๋ž˜์Šค๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@WebMvcTest // ๊ธ€๋กœ๋ฒŒ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š” ํฌํ•จ X
@AutoConfigureMockMvc
class ExceptionApiControllerTest {

    @Test
    fun postTest() {
        val userRequest = UserRequest().apply{
            this.name = "JuHyun"
            this.age = 20
            this.phoneNumber = "010-1111-2222"
            this.address = "์„œ์šธ"
            this.email = "a@a.com"
            this.createdAt = "2021-09-06 21:11:12"
        }

        // JSON ํ˜•์‹์˜ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
        val json = jacksonObjectMapper().writeValueAsString(userRequest)
        println(json)

        val uri = "/api/exception"

        mockMvc.perform(
                post(uri)
                        .content(json)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk)
                .andExpect(jsonPath("\$.name").value("JuHyun"))
                .andExpect(jsonPath("\$.age").value("20"))
                .andExpect(jsonPath("\$.address").value("์„œ์šธ"))
                .andDo(print())
    }

 

 

 

 

โ˜… ์ •๋ฆฌ

์ด์ƒ์œผ๋กœ Spring Boot์—์„œ MockMvc๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊ฐ„๋žตํžˆ ์ •๋ฆฌ๋ฅผ ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

์˜ˆ์ œ์—์„œ๋Š” ์ž‘์„ฑํ•˜์ง€ ์•Š์€ PUT, DELETE ๋ฉ”์†Œ๋“œ๋„ ์œ„์™€ ๋น„์Šทํ•œ ๋ฐฉ์‹์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ํฌ์ŠคํŒ…์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ๋ฉ”์†Œ๋“œ๋„ ๋งŽ์ด ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€์ ์ธ ๋‚ด์šฉ์€ ์•„๋ž˜ ๊ณต์‹๋ฌธ์„œ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”

 

 

โ˜… References

 

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€