이번엔 테스트를 먼저 생성해 봅시다.
BankControllerTest로 갑시다.
@Nested
@DisplayName("PATCH /api/banks")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class PatchExistingBank {
@Test
fun `should update an existing bank`() {
// given
val updatedBank = Bank("1234", 1.0, 1)
// when
val performPatchRequest = mockMvc.patch(baseUrl) {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(updatedBank)
}
// then
performPatchRequest
.andDo { print() }
.andExpect {
status { isOk() }
}
}
}
여태 해왔던 방식과 동일합니다.

코드 200이 아닌 405가 들어와서 실패했습니다.
405는 Method Not Allowed로 이 메서드를 지원하지 않아 발생하게 됩니다.
우리의 경우는 Patch 맵핑이 되어있지 않아 발생하게 됩니다.
@PatchMapping
fun updateBank(@RequestBody bank: Bank): Bank = bank //TODO
BankController에 해당 맵핑을 추가해 준 뒤 다시 실행해 봅시다.

이번엔 맵핑이 되어있어 통과하게 됩니다.
다시 테스트로 가서 들어온 정보와 입력된 정보가 일치하는지 비교해 봅시다.
performPost.andDo { print() }
.andExpect {
status { isCreated() }
content { contentType(MediaType.APPLICATION_JSON) }
jsonPath("$.accountNumber") { value("acc123") }
jsonPath("$.trust") { value("31.415") }
jsonPath("$.transactionFee") { value("2") }
}
테스트 코드를 jsonPath로 일일이 검사하는 방법도 있지만,
performPatchRequest
.andDo { print() }
.andExpect {
status { isOk() }
content {
contentType(MediaType.APPLICATION_JSON)
json(objectMapper.writeValueAsString(updatedBank))
}
}
위와 같이 content 안에 넣어 한번에 비교할 수도 있습니다.
저렇게 여러 과정을 나눠서 할 필요 없이 짧게도 가능합니다.
mockMvc.get("$baseUrl/${updatedBank.accountNumber}")
.andExpect { content { { json(objectMapper.writeValueAsString(updatedBank)) } } }
get을 이용해 해당 bank 정보를 가져온 다음 입력한 정보와 일치하는지 비교하는 구문입니다.
@Test
fun `should add the new bank`() {
// given
val newBank = Bank("acc123", 31.415, 2)
// when
val performPost = mockMvc.post(baseUrl) {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(newBank)
}
// then
performPost.andDo { print() }
.andExpect {
status { isCreated() }
content {
contentType(MediaType.APPLICATION_JSON)
json(objectMapper.writeValueAsString(newBank))
}
}
mockMvc.get("$baseUrl/${newBank.accountNumber}")
.andExpect { content { json(objectMapper.writeValueAsString(newBank)) } }
}
should add the new bank(Bank 추가 테스트) 또한 변경해 주도록 합시다.
작동되는 걸 확인했으니 BankController로 가서 updateBank가 실제로 작동하도록 변경해 줍시다.
BankController
package com.study.hello_world.controller
import com.study.hello_world.model.Bank
import com.study.hello_world.service.BankService
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/api/banks")
class BankController(private val service: BankService) {
@ExceptionHandler(NoSuchElementException::class)
fun handleNotFound(e: NoSuchElementException): ResponseEntity<String> =
ResponseEntity(e.message, HttpStatus.NOT_FOUND)
@ExceptionHandler(IllegalArgumentException::class)
fun handleNotFound(e: IllegalArgumentException): ResponseEntity<String> =
ResponseEntity(e.message, HttpStatus.BAD_REQUEST)
@GetMapping
fun helloWorld(): Collection<Bank> = service.getBanks()
@GetMapping("/{accountNumber}")
fun getBBank(@PathVariable accountNumber: String) = service.getBank(accountNumber)
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun addBank(@RequestBody bank: Bank): Bank = service.addBank(bank)
@PatchMapping
fun updateBank(@RequestBody bank: Bank): Bank = service.updateBank(bank)
}
BankService
package com.study.hello_world.service
import com.study.hello_world.datasource.BankDataSource
import com.study.hello_world.model.Bank
import org.springframework.stereotype.Service
@Service
class BankService(private val dataSource: BankDataSource) {
fun getBanks(): Collection<Bank> {
return dataSource.retrieveBanks()
}
fun getBank(accountNumber: String): Bank = dataSource.retrieveBank(accountNumber)
fun addBank(bank: Bank): Bank = dataSource.createBank(bank)
fun updateBank(bank: Bank): Bank = dataSource.updateBank(bank)
}
BankDataSource
package com.study.hello_world.datasource
import com.study.hello_world.model.Bank
interface BankDataSource {
fun retrieveBanks(): Collection<Bank>
fun retrieveBank(accountNumber: String): Bank
fun createBank(bank: Bank): Bank
fun updateBank(bank: Bank): Bank
}
MockBankDataSource
package com.study.hello_world.datasource.mock
import com.study.hello_world.datasource.BankDataSource
import com.study.hello_world.model.Bank
import org.springframework.stereotype.Repository
@Repository
class MockBankDataSource : BankDataSource {
val banks = mutableListOf(
Bank("1234", 3.14, 18),
Bank("110", 17.0, 0),
Bank("5678", 0.0, 100),
)
override fun retrieveBanks(): Collection<Bank> = banks
override fun retrieveBank(accountNumber: String): Bank =
banks.firstOrNull() { it.accountNumber == accountNumber }
?: throw NoSuchElementException("Could not find a bank with account number $accountNumber aaa")
override fun createBank(bank: Bank): Bank {
if (banks.any { it.accountNumber == bank.accountNumber }) {
throw IllegalArgumentException("Bank with account number ${bank.accountNumber} already exist")
}
banks.add(bank)
return bank
}
override fun updateBank(bank: Bank): Bank {
val currentBank = banks.firstOrNull { it.accountNumber == bank.accountNumber }
?: throw NoSuchElementException("Could not find a bank with account number $bank.accountNumber aaa")
banks.remove(currentBank)
banks.add(bank)
return bank
}
}
이번엔 잘못된 요청이 들어왔을 때를 테스트해봅시다.
@Test
fun `should return BAD REQUEST if no bank with given account number exists`() {
// given
val invalidBank = Bank("does_not_exist", 1.0, 1)
// when
val performPatchRequest = mockMvc.patch(baseUrl) {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(invalidBank)
}
// then
performPatchRequest
.andDo { print() }
.andExpect { status { isBadRequest() } }
}
patchExistingBank 클래스 안에 해당 테스트를 추가해 줍니다.

400이 아닌 404가 들어왔다고 합니다.
이전에 NoSuchElementException을 isNotFound()로 예외처리 한 적이 있으므로 테스트를 변경해 줍시다.
이전에 진행했던 예외처리
ExceptionHandler(NoSuchElementException::class)
fun handleNotFound(e: NoSuchElementException): ResponseEntity<String> =
ResponseEntity(e.message, HttpStatus.NOT_FOUND)
옳지 않은 accountNumber Patch
@Test
fun `should return BAD REQUEST if no bank with given account number exists`() {
// given
val invalidBank = Bank("does_not_exist", 1.0, 1)
// when
val performPatchRequest = mockMvc.patch(baseUrl) {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(invalidBank)
}
// then
performPatchRequest
.andDo { print() }
.andExpect { status { isNotFound() } }
}
'서버 > Kotlin-Spring_Boot' 카테고리의 다른 글
| QueryDsl jakarta 설정 방법 (0) | 2023.02.10 |
|---|---|
| Kotlin-Spring_Boot 강의 정리) 10. DELETE Endpoint (0) | 2023.02.03 |
| Kotlin-Spring_Boot 강의 정리) 8. POST Endpoint (1) | 2023.01.31 |
| Kotlin-Spring_Boot 강의 정리) 7. GET Single Bank (0) | 2023.01.30 |
| Kotlin-Spring_Boot 강의 정리) 6. Web Layer (0) | 2023.01.30 |
댓글