[#8] feat: Cafe 생성 메소드 리팩토링

- Cafe.createCafe 메소드 리팩토링
- Cafe 기본적인 값 일치 테스트코드 추가
This commit is contained in:
beaniejoy
2022-08-08 00:10:43 +09:00
parent a18dfdf193
commit bae794a5aa
8 changed files with 326 additions and 208 deletions

View File

@@ -27,7 +27,7 @@ class CafeController(
}
@GetMapping
fun searchCafeList(
fun searchCafe(
@PageableDefault(sort = ["name"], direction = Sort.Direction.ASC, page = 0, size = 10) pageable: Pageable
): Page<CafeSearchResponseDto> {
return cafeService.getCafeList(pageable)

View File

@@ -49,21 +49,11 @@ class Cafe protected constructor(
description: String,
cafeMenuRequestList: List<CafeMenuInfoRequestDto>,
): Cafe {
val cafeMenuList = cafeMenuRequestList.map { cafeMenuRequestDto ->
val cafeMenuEntityList = cafeMenuRequestList.map { cafeMenuRequestDto ->
CafeMenu.createCafeMenu(
name = cafeMenuRequestDto.name!!,
price = cafeMenuRequestDto.price,
menuOptionList = cafeMenuRequestDto.menuOptionList.map { menuOptionRequestDto ->
MenuOption.createMenuOption(
title = menuOptionRequestDto.title,
optionDetailList = menuOptionRequestDto.optionDetailList.map { optionDetailRequestDto ->
OptionDetail.createOptionDetail(
name = optionDetailRequestDto.name,
extraPrice = optionDetailRequestDto.extraPrice
)
}
)
}
menuOptionRequestList = cafeMenuRequestDto.menuOptionList
)
}
@@ -73,7 +63,7 @@ class Cafe protected constructor(
phoneNumber = phoneNumber,
description = description
).apply {
cafeMenuList.forEach { this.addCafeMenu(it) }
cafeMenuEntityList.forEach { this.addCafeMenu(it) }
}
}
}

View File

@@ -1,6 +1,7 @@
package io.beaniejoy.dongnecafe.domain.cafe.entity
import io.beaniejoy.dongnecafe.common.entity.BaseTimeEntity
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.MenuOptionInfoRequestDto
import java.math.BigDecimal
import javax.persistence.*
@@ -29,12 +30,19 @@ class CafeMenu protected constructor(
val menuOptionList: MutableList<MenuOption> = arrayListOf()
companion object {
fun createCafeMenu(name: String, price: BigDecimal, menuOptionList: List<MenuOption>): CafeMenu {
fun createCafeMenu(name: String, price: BigDecimal, menuOptionRequestList: List<MenuOptionInfoRequestDto>): CafeMenu {
val menuOptionEntityList = menuOptionRequestList.map { menuOptionRequestDto ->
MenuOption.createMenuOption(
title = menuOptionRequestDto.title,
optionDetailRequestList = menuOptionRequestDto.optionDetailList
)
}
return CafeMenu(
name = name,
price = price
).apply {
menuOptionList.forEach { this.addMenuOption(it) }
menuOptionEntityList.forEach { this.addMenuOption(it) }
}
}
}

View File

@@ -1,6 +1,7 @@
package io.beaniejoy.dongnecafe.domain.cafe.entity
import io.beaniejoy.dongnecafe.common.entity.BaseTimeEntity
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.OptionDetailInfoRequestDto
import javax.persistence.*
@Entity
@@ -24,11 +25,18 @@ class MenuOption protected constructor(
val optionDetailList: MutableList<OptionDetail> = arrayListOf()
companion object {
fun createMenuOption(title: String, optionDetailList: List<OptionDetail>): MenuOption {
fun createMenuOption(title: String, optionDetailRequestList: List<OptionDetailInfoRequestDto>): MenuOption {
val optionDetailEntityList = optionDetailRequestList.map { optionDetailRequestDto ->
OptionDetail.createOptionDetail(
name = optionDetailRequestDto.name,
extraPrice = optionDetailRequestDto.extraPrice
)
}
return MenuOption(
title = title
).apply {
optionDetailList.forEach { this.addOptionDetail(it) }
optionDetailEntityList.forEach { this.addOptionDetail(it) }
}
}
}

View File

@@ -32,6 +32,7 @@ class CafeService(
* - 메뉴 옵션 (옵션 이름 /ex. 사이즈)
* - 옵션 상세 (상세 이름, 추가 금액 /ex. [(medium, 0), (large, 200), (venti, 700)])
*/
@Transactional
fun createCafe(
name: String,
address: String,

View File

@@ -0,0 +1,106 @@
package io.beaniejoy.dongnecafe.domain.cafe.entity
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.CafeInfoRequestDto
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.CafeMenuInfoRequestDto
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.MenuOptionInfoRequestDto
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.OptionDetailInfoRequestDto
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import java.math.BigDecimal
internal class CafeTest {
@Test
fun create_cafe_test() {
val cafeRequestDto = createCreateCafeRequestDto()
val cafe = Cafe.createCafe(
name = cafeRequestDto.name!!,
address = cafeRequestDto.address!!,
phoneNumber = cafeRequestDto.phoneNumber!!,
description = cafeRequestDto.description!!,
cafeMenuRequestList = cafeRequestDto.cafeMenuList
)
assertEquals(cafeRequestDto.name, cafe.name)
assertEquals(cafeRequestDto.address, cafe.address)
assertEquals(cafeRequestDto.phoneNumber, cafe.phoneNumber)
assertEquals(cafeRequestDto.description, cafe.description)
assertCafeMenuListEquals(cafeRequestDto.cafeMenuList, cafe.cafeMenuList)
}
private fun assertCafeMenuListEquals(
cafeMenuRequestList: List<CafeMenuInfoRequestDto>,
cafeMenuList: List<CafeMenu>,
) {
for (index in cafeMenuRequestList.indices) {
assertEquals(cafeMenuRequestList[index].name, cafeMenuList[index].name)
assertEquals(cafeMenuRequestList[index].price, cafeMenuList[index].price)
assertMenuOptionListEquals(cafeMenuRequestList[index].menuOptionList, cafeMenuList[index].menuOptionList)
}
}
private fun assertMenuOptionListEquals(
menuOptionRequestList: List<MenuOptionInfoRequestDto>,
menuOptionList: List<MenuOption>,
) {
for (index in menuOptionRequestList.indices) {
assertEquals(menuOptionRequestList[index].title, menuOptionList[index].title)
assertOptionDetailListEquals(
menuOptionRequestList[index].optionDetailList,
menuOptionList[index].optionDetailList
)
}
}
private fun assertOptionDetailListEquals(
optionDetailRequestList: List<OptionDetailInfoRequestDto>,
optionDetailList: MutableList<OptionDetail>,
) {
for (index in optionDetailRequestList.indices) {
assertEquals(optionDetailRequestList[index].name, optionDetailList[index].name)
assertEquals(optionDetailRequestList[index].extraPrice, optionDetailList[index].extraPrice)
}
}
private fun createCreateCafeRequestDto(): CafeInfoRequestDto {
val cafeName = "beanie_cafe"
val cafeAddress = "beanie_cafe_address"
val phoneNumber = "01012345678"
val description = "beanie_cafe_description"
val sizeOptionDetailList = listOf(
OptionDetailInfoRequestDto(name = "medium", extraPrice = BigDecimal.ZERO),
OptionDetailInfoRequestDto(name = "large", extraPrice = BigDecimal.valueOf(200L)),
OptionDetailInfoRequestDto(name = "venti", extraPrice = BigDecimal.valueOf(700L))
)
val sizeMenuOption = MenuOptionInfoRequestDto(
title = "size",
optionDetailList = sizeOptionDetailList
)
val cafeMenuList = listOf(
CafeMenuInfoRequestDto(
name = "menu1",
price = BigDecimal.valueOf(2_800L),
menuOptionList = listOf(sizeMenuOption)
),
CafeMenuInfoRequestDto(
name = "menu2",
price = BigDecimal.valueOf(3_500L),
menuOptionList = listOf(sizeMenuOption)
),
)
return CafeInfoRequestDto(
name = cafeName,
address = cafeAddress,
phoneNumber = phoneNumber,
description = description,
cafeMenuList = cafeMenuList
)
}
}

View File

@@ -0,0 +1,193 @@
package io.beaniejoy.dongnecafe.domain.cafe.service
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.CafeInfoRequestDto
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.CafeMenuInfoRequestDto
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.MenuOptionInfoRequestDto
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.OptionDetailInfoRequestDto
import io.beaniejoy.dongnecafe.domain.cafe.entity.Cafe
import io.beaniejoy.dongnecafe.domain.cafe.error.CafeExistedException
import io.beaniejoy.dongnecafe.domain.cafe.error.CafeNotFoundException
import io.beaniejoy.dongnecafe.domain.cafe.repository.CafeRepository
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.MethodOrderer
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestMethodOrder
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.junit.jupiter.MockitoExtension
import java.math.BigDecimal
import java.util.*
import javax.persistence.GeneratedValue
@ExtendWith(MockitoExtension::class)
@TestMethodOrder(MethodOrderer.DisplayName::class)
internal class CafeServiceMockTest {
@InjectMocks
lateinit var mockCafeService: CafeService
@Mock
lateinit var mockCafeRepository: CafeRepository
@Test
@DisplayName("카페 신규 생성 테스트")
fun create_cafe_test() {
// given
val cafeRequestDto = createCreateCafeRequestDto()
val savedMockCafeId = 100L
`when`(mockCafeRepository.findByName(cafeRequestDto.name!!)).thenReturn(null)
`when`(mockCafeRepository.save(any(Cafe::class.java))).thenAnswer {
injectCafeId(it.getArgument(0), savedMockCafeId)
}
// when
val savedCafeId = mockCafeService.createCafe(
name = cafeRequestDto.name!!,
address = cafeRequestDto.address!!,
phoneNumber = cafeRequestDto.phoneNumber!!,
description = cafeRequestDto.description!!,
cafeMenuRequestList = cafeRequestDto.cafeMenuList
)
// then
verify(mockCafeRepository).findByName(cafeRequestDto.name!!) // TODO eq 에러 발생 이유
verify(mockCafeRepository).save(any(Cafe::class.java))
assertEquals(savedCafeId, savedMockCafeId)
}
@Test
@DisplayName("카페 신규 생성시 이미 존재하는 카페 예외 발생 테스트")
fun fail_create_cafe_when_existed() {
// given
val cafeRequestDto = createCreateCafeRequestDto()
val cafe = Cafe.createCafe(
name = cafeRequestDto.name!!,
address = cafeRequestDto.address!!,
phoneNumber = cafeRequestDto.phoneNumber!!,
description = cafeRequestDto.description!!,
cafeMenuRequestList = cafeRequestDto.cafeMenuList
)
`when`(mockCafeRepository.findByName(cafeRequestDto.name!!)).thenReturn(cafe)
// then
assertThrows<CafeExistedException> {
// when
mockCafeService.createCafe(
name = cafeRequestDto.name!!,
address = cafeRequestDto.address!!,
phoneNumber = cafeRequestDto.phoneNumber!!,
description = cafeRequestDto.description!!,
cafeMenuRequestList = cafeRequestDto.cafeMenuList
)
}
}
@Test
@DisplayName("카페 정보 변경 테스트")
fun update_cafe_test() {
// given
val cafeRequestDto = createCreateCafeRequestDto()
val cafe = Cafe.createCafe(
name = cafeRequestDto.name!!,
address = cafeRequestDto.address!!,
phoneNumber = cafeRequestDto.phoneNumber!!,
description = cafeRequestDto.description!!,
cafeMenuRequestList = cafeRequestDto.cafeMenuList
)
val cafeId = 50L
// TODO findByIdOrNull은 kotlin test 라이브러리 필요한 듯
`when`(mockCafeRepository.findById(cafeId)).thenReturn(Optional.of(cafe))
// then
mockCafeService.updateCafe(
id = cafeId,
name = "",
address = "",
phoneNumber = "",
description = "",
)
verify(mockCafeRepository).findById(eq(cafeId))
// TODO update TEST 방법?
}
@Test
@DisplayName("카페 정보 변경시 존재하지 않는 카페 예외 발생 테스트")
fun fail_update_cafe_when_not_found() {
// given
val cafeId = 50L
`when`(mockCafeRepository.findById(cafeId)).thenReturn(Optional.empty())
assertThrows<CafeNotFoundException> {
mockCafeService.updateCafe(
id = cafeId,
name = "",
address = "",
phoneNumber = "",
description = "",
)
}
}
private fun createCreateCafeRequestDto(): CafeInfoRequestDto {
val cafeName = "beanie_cafe"
val cafeAddress = "beanie_cafe_address"
val phoneNumber = "01012345678"
val description = "beanie_cafe_description"
val sizeOptionDetailList = listOf(
OptionDetailInfoRequestDto(name = "medium", extraPrice = BigDecimal.ZERO),
OptionDetailInfoRequestDto(name = "large", extraPrice = BigDecimal.valueOf(200L)),
OptionDetailInfoRequestDto(name = "venti", extraPrice = BigDecimal.valueOf(700L))
)
val sizeMenuOption = MenuOptionInfoRequestDto(
title = "size",
optionDetailList = sizeOptionDetailList
)
val cafeMenuList = listOf(
CafeMenuInfoRequestDto(
name = "menu1",
price = BigDecimal.valueOf(2_800L),
menuOptionList = listOf(sizeMenuOption)
),
CafeMenuInfoRequestDto(
name = "menu2",
price = BigDecimal.valueOf(3_500L),
menuOptionList = listOf(sizeMenuOption)
),
)
return CafeInfoRequestDto(
name = cafeName,
address = cafeAddress,
phoneNumber = phoneNumber,
description = description,
cafeMenuList = cafeMenuList
)
}
private fun injectCafeId(
cafe: Cafe,
newCafeId: Long,
): Cafe {
val idField = cafe.javaClass.declaredFields
.find { f ->
f.getAnnotation(GeneratedValue::class.java) != null
} ?: return cafe
idField.isAccessible = true
idField.set(cafe, newCafeId)
return cafe
}
}

View File

@@ -1,193 +1,5 @@
package io.beaniejoy.dongnecafe.domain.cafe.service
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.CafeInfoRequestDto
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.CafeMenuInfoRequestDto
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.MenuOptionInfoRequestDto
import io.beaniejoy.dongnecafe.domain.cafe.dto.request.OptionDetailInfoRequestDto
import io.beaniejoy.dongnecafe.domain.cafe.entity.Cafe
import io.beaniejoy.dongnecafe.domain.cafe.error.CafeExistedException
import io.beaniejoy.dongnecafe.domain.cafe.error.CafeNotFoundException
import io.beaniejoy.dongnecafe.domain.cafe.repository.CafeRepository
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.MethodOrderer
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestMethodOrder
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.junit.jupiter.MockitoExtension
import java.math.BigDecimal
import java.util.*
import javax.persistence.GeneratedValue
@ExtendWith(MockitoExtension::class)
@TestMethodOrder(MethodOrderer.DisplayName::class)
internal class CafeServiceTest {
@InjectMocks
lateinit var mockCafeService: CafeService
@Mock
lateinit var mockCafeRepository: CafeRepository
@Test
@DisplayName("카페 신규 생성 테스트")
fun create_cafe_test() {
// given
val cafeRequestDto = createCreateCafeRequestDto()
val savedMockCafeId = 100L
`when`(mockCafeRepository.findByName(cafeRequestDto.name!!)).thenReturn(null)
`when`(mockCafeRepository.save(any(Cafe::class.java))).thenAnswer {
injectCafeId(it.getArgument(0), savedMockCafeId)
}
// when
val savedCafeId = mockCafeService.createCafe(
name = cafeRequestDto.name!!,
address = cafeRequestDto.address!!,
phoneNumber = cafeRequestDto.phoneNumber!!,
description = cafeRequestDto.description!!,
cafeMenuRequestList = cafeRequestDto.cafeMenuList
)
// then
verify(mockCafeRepository).findByName(cafeRequestDto.name!!) // TODO eq 에러 발생 이유
verify(mockCafeRepository).save(any(Cafe::class.java))
assertEquals(savedCafeId, savedMockCafeId)
}
@Test
@DisplayName("카페 신규 생성시 이미 존재하는 카페 예외 발생 테스트")
fun fail_create_cafe_when_existed() {
// given
val cafeRequestDto = createCreateCafeRequestDto()
val cafe = Cafe.createCafe(
name = cafeRequestDto.name!!,
address = cafeRequestDto.address!!,
phoneNumber = cafeRequestDto.phoneNumber!!,
description = cafeRequestDto.description!!,
cafeMenuRequestList = cafeRequestDto.cafeMenuList
)
`when`(mockCafeRepository.findByName(cafeRequestDto.name!!)).thenReturn(cafe)
// then
assertThrows<CafeExistedException> {
// when
mockCafeService.createCafe(
name = cafeRequestDto.name!!,
address = cafeRequestDto.address!!,
phoneNumber = cafeRequestDto.phoneNumber!!,
description = cafeRequestDto.description!!,
cafeMenuRequestList = cafeRequestDto.cafeMenuList
)
}
}
@Test
@DisplayName("카페 정보 변경 테스트")
fun update_cafe_test() {
// given
val cafeRequestDto = createCreateCafeRequestDto()
val cafe = Cafe.createCafe(
name = cafeRequestDto.name!!,
address = cafeRequestDto.address!!,
phoneNumber = cafeRequestDto.phoneNumber!!,
description = cafeRequestDto.description!!,
cafeMenuRequestList = cafeRequestDto.cafeMenuList
)
val cafeId = 50L
// TODO findByIdOrNull은 kotlin test 라이브러리 필요한 듯
`when`(mockCafeRepository.findById(cafeId)).thenReturn(Optional.of(cafe))
// then
mockCafeService.updateCafe(
id = cafeId,
name = "",
address = "",
phoneNumber = "",
description = "",
)
verify(mockCafeRepository).findById(eq(cafeId))
// TODO update TEST 방법?
}
@Test
@DisplayName("카페 정보 변경시 존재하지 않는 카페 예외 발생 테스트")
fun fail_update_cafe_when_not_found() {
// given
val cafeId = 50L
`when`(mockCafeRepository.findById(cafeId)).thenReturn(Optional.empty())
assertThrows<CafeNotFoundException> {
mockCafeService.updateCafe(
id = cafeId,
name = "",
address = "",
phoneNumber = "",
description = "",
)
}
}
private fun createCreateCafeRequestDto(): CafeInfoRequestDto {
val cafeName = "beanie_cafe"
val cafeAddress = "beanie_cafe_address"
val phoneNumber = "01012345678"
val description = "beanie_cafe_description"
val sizeOptionDetailList = listOf(
OptionDetailInfoRequestDto(name = "medium", extraPrice = BigDecimal.ZERO),
OptionDetailInfoRequestDto(name = "large", extraPrice = BigDecimal.valueOf(200L)),
OptionDetailInfoRequestDto(name = "venti", extraPrice = BigDecimal.valueOf(700L))
)
val sizeMenuOption = MenuOptionInfoRequestDto(
title = "size",
optionDetailList = sizeOptionDetailList
)
val cafeMenuList = listOf(
CafeMenuInfoRequestDto(
name = "menu1",
price = BigDecimal.valueOf(2_800L),
menuOptionList = listOf(sizeMenuOption)
),
CafeMenuInfoRequestDto(
name = "menu2",
price = BigDecimal.valueOf(3_500L),
menuOptionList = listOf(sizeMenuOption)
),
)
return CafeInfoRequestDto(
name = cafeName,
address = cafeAddress,
phoneNumber = phoneNumber,
description = description,
cafeMenuList = cafeMenuList
)
}
private fun injectCafeId(
cafe: Cafe,
newCafeId: Long,
): Cafe {
val idField = cafe.javaClass.declaredFields
.find { f ->
f.getAnnotation(GeneratedValue::class.java) != null
} ?: return cafe
idField.isAccessible = true
idField.set(cafe, newCafeId)
return cafe
}
// TODO spring jpa 연동 테스트 (h2 테스트 DB 사용)
class CafeServiceTest {
}