diff --git a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeMenuService.kt b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeMenuService.kt index 37545cc..ee4b91c 100644 --- a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeMenuService.kt +++ b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeMenuService.kt @@ -9,11 +9,12 @@ import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @Service -@Transactional(readOnly = true) +@Transactional class CafeMenuService( private val cafeMenuRepository: CafeMenuRepository, private val menuOptionService: MenuOptionService ) { + @Transactional(readOnly = true) fun getDetailedInfoByMenuId(menuId: Long, cafeId: Long): CafeMenuDetailedInfo { val cafeMenu = cafeMenuRepository.findByIdOrNull(menuId) ?: throw CafeMenuNotFoundException(menuId = menuId, cafeId = cafeId) @@ -21,7 +22,6 @@ class CafeMenuService( return CafeMenuDetailedInfo.of(cafeMenu) } - @Transactional fun updateInfoAndBulkUpdate(menuId: Long, cafeId: Long, resource: CafeMenuUpdateRequest) { val cafeMenu = cafeMenuRepository.findByIdOrNull(menuId) ?: throw CafeMenuNotFoundException(menuId = menuId, cafeId = cafeId) @@ -31,7 +31,6 @@ class CafeMenuService( menuOptionService.bulkUpdate(resource.menuOptionList) } - @Transactional fun deleteByCafeMenuId(menuId: Long, cafeId: Long) { val cafeMenu = cafeMenuRepository.findByIdOrNull(menuId) ?: throw CafeMenuNotFoundException(menuId = menuId, cafeId = cafeId) @@ -39,7 +38,6 @@ class CafeMenuService( cafeMenuRepository.delete(cafeMenu) } - @Transactional fun bulkDelete(cafeId: Long, cafeMenuIdList: List) { cafeMenuIdList.forEach { deleteByCafeMenuId(menuId = it, cafeId = cafeId) diff --git a/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeMenuServiceTest.kt b/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeMenuServiceTest.kt index 2dea392..28281fe 100644 --- a/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeMenuServiceTest.kt +++ b/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeMenuServiceTest.kt @@ -1,57 +1,96 @@ package io.beaniejoy.dongnecafe.domain.cafe.service -import io.beaniejoy.dongnecafe.domain.cafe.entity.Cafe -import io.beaniejoy.dongnecafe.domain.cafe.model.request.CafeMenuUpdateRequest +import io.beaniejoy.dongnecafe.domain.cafe.entity.CafeMenu +import io.beaniejoy.dongnecafe.domain.cafe.error.CafeMenuNotFoundException import io.beaniejoy.dongnecafe.domain.cafe.repository.CafeMenuRepository -import io.beaniejoy.dongnecafe.domain.cafe.repository.CafeRepository -import io.beaniejoy.dongnecafe.domain.cafe.utils.CafeTestUtils -import org.junit.jupiter.api.BeforeEach +import io.beaniejoy.dongnecafe.domain.cafe.repository.MenuOptionRepository +import io.beaniejoy.dongnecafe.domain.cafe.repository.OptionDetailRepository +import io.beaniejoy.dongnecafe.domain.cafe.utils.CafeMenuTestUtils +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.test.annotation.Rollback -import org.springframework.transaction.annotation.Transactional +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.verify +import org.mockito.Mockito.`when` +import org.mockito.junit.jupiter.MockitoExtension +import java.util.* -@SpringBootTest +@ExtendWith(MockitoExtension::class) internal class CafeMenuServiceTest { - @Autowired - lateinit var cafeMenuRepository: CafeMenuRepository - @Autowired - lateinit var cafeRepository: CafeRepository - @Autowired - lateinit var cafeMenuService: CafeMenuService + @Mock + lateinit var mockCafeMenuRepository: CafeMenuRepository - @BeforeEach - fun setup() { - val cafeRegisterRequest = CafeTestUtils.createCafeRegisterRequest() + @Mock + lateinit var mockMenuOptionRepository: MenuOptionRepository - cafeRepository.save( - Cafe.createCafe( - name = cafeRegisterRequest.name!!, - address = cafeRegisterRequest.address!!, - phoneNumber = cafeRegisterRequest.phoneNumber!!, - description = cafeRegisterRequest.description!!, - cafeMenuRequestList = cafeRegisterRequest.cafeMenuList - ) + @Mock + lateinit var mockOptionDetailRepository: OptionDetailRepository + + @Mock + lateinit var mockOptionDetailService: OptionDetailService + + @Mock + lateinit var mockMenuOptionService: MenuOptionService + + @InjectMocks + lateinit var mockCafeMenuService: CafeMenuService + + + + + @Test + @DisplayName("카페 메뉴 ID를 통한 카페 메뉴 상세 조회") + fun find_cafe_menu_by_cafe_menu_id() { + // given + val (name, price, menuOptionList) = CafeMenuTestUtils.createCafeMenuRegisterRequest() + val cafeMenu = CafeMenu.createCafeMenu( + name = name!!, + price = price, + menuOptionRequestList = menuOptionList ) + + val findCafeId = 100L + val findCafeMenuId = 1001L + + `when`(mockCafeMenuRepository.findById(findCafeMenuId)) + .thenReturn(Optional.of(cafeMenu.apply { + // cafe_menu_id Reflection 주입 + CafeMenuTestUtils.injectCafeMenuId(this, findCafeMenuId) + })) + + // when + val cafeMenuDetailedInfo = mockCafeMenuService.getDetailedInfoByMenuId(findCafeMenuId, findCafeId) + + // then + verify(mockCafeMenuRepository).findById(findCafeMenuId) + + cafeMenuDetailedInfo.also { + assertEquals(findCafeMenuId, it.cafeMenuId) + assertEquals(name, it.name) + assertEquals(price, it.price) + // TODO: MenuOption list도 비교 검증해야되는지 + } } @Test - @Transactional - @Rollback(false) - fun updateTest() { - val cafeMenu = cafeRepository.findByName("beanie_cafe")!!.cafeMenuList[0] + @DisplayName("존재하지 않는 카페 조회에 대한 예외 발생 테스트") + fun find_cafe_menu_by_cafe_menu_id_when_not_existed() { + // given + val findCafeId = 100L + val findCafeMenuId = 1001L + `when`(mockCafeMenuRepository.findById(findCafeMenuId)).thenReturn(Optional.empty()) - cafeMenuService.updateInfoAndBulkUpdate( - menuId = cafeMenu.id, - cafeId = cafeMenu.cafe!!.id, - resource = CafeMenuUpdateRequest(name = "updated_name", price = cafeMenu.price) - ) - } + // then + val exception = assertThrows { + // when + mockCafeMenuService.getDetailedInfoByMenuId(findCafeMenuId, findCafeId) + } - @Test - @Transactional - fun deleteTest() { - cafeMenuRepository.deleteById(100L) + assertEquals("Cafe[${findCafeId}]의 Menu[${findCafeMenuId}]는 존재하지 않는 메뉴입니다.", exception.message) + + verify(mockCafeMenuRepository).findById(findCafeMenuId) } -} \ No newline at end of file +} diff --git a/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeServiceTest.kt b/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeServiceTest.kt index d48b0c1..233af8d 100644 --- a/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeServiceTest.kt +++ b/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeServiceTest.kt @@ -15,6 +15,7 @@ import org.mockito.junit.jupiter.MockitoExtension import java.util.* import javax.persistence.GeneratedValue +// TODO: BDD 스타일로 리팩토링 해볼 것(추후 mockK도 사용해보자) @ExtendWith(MockitoExtension::class) @TestMethodOrder(MethodOrderer.DisplayName::class) internal class CafeServiceTest { @@ -28,25 +29,25 @@ internal class CafeServiceTest { @DisplayName("카페 신규 생성 테스트") fun create_cafe_test() { // given - val cafeRequestDto = CafeTestUtils.createCafeRegisterRequest() + val (name, address, phoneNumber, description, cafeMenuList) = CafeTestUtils.createCafeRegisterRequest() val savedMockCafeId = 100L - `when`(mockCafeRepository.findByName(cafeRequestDto.name!!)).thenReturn(null) + `when`(mockCafeRepository.findByName(name!!)).thenReturn(null) `when`(mockCafeRepository.save(any(Cafe::class.java))).thenAnswer { - injectCafeId(it.getArgument(0), savedMockCafeId) + CafeTestUtils.injectCafeId(it.getArgument(0), savedMockCafeId) } // when val savedCafeId = mockCafeService.createNew( - name = cafeRequestDto.name!!, - address = cafeRequestDto.address!!, - phoneNumber = cafeRequestDto.phoneNumber!!, - description = cafeRequestDto.description!!, - cafeMenuRequestList = cafeRequestDto.cafeMenuList + name = name, + address = address!!, + phoneNumber = phoneNumber!!, + description = description!!, + cafeMenuRequestList = cafeMenuList ) // then - verify(mockCafeRepository).findByName(cafeRequestDto.name!!) // TODO eq 에러 발생 이유 + verify(mockCafeRepository).findByName(name) // TODO eq 에러 발생 이유 verify(mockCafeRepository).save(any(Cafe::class.java)) assertEquals(savedCafeId, savedMockCafeId) @@ -56,61 +57,74 @@ internal class CafeServiceTest { @DisplayName("카페 신규 생성시 이미 존재하는 카페 예외 발생 테스트") fun fail_create_cafe_when_existed() { // given - val cafeRequestDto = CafeTestUtils.createCafeRegisterRequest() + val (name, address, phoneNumber, description, cafeMenuList) = CafeTestUtils.createCafeRegisterRequest() val cafe = Cafe.createCafe( - name = cafeRequestDto.name!!, - address = cafeRequestDto.address!!, - phoneNumber = cafeRequestDto.phoneNumber!!, - description = cafeRequestDto.description!!, - cafeMenuRequestList = cafeRequestDto.cafeMenuList + name = name!!, + address = address!!, + phoneNumber = phoneNumber!!, + description = description!!, + cafeMenuRequestList = cafeMenuList ) - `when`(mockCafeRepository.findByName(cafeRequestDto.name!!)).thenReturn(cafe) + `when`(mockCafeRepository.findByName(name)).thenReturn(cafe) // then assertThrows { // when mockCafeService.createNew( - name = cafeRequestDto.name!!, - address = cafeRequestDto.address!!, - phoneNumber = cafeRequestDto.phoneNumber!!, - description = cafeRequestDto.description!!, - cafeMenuRequestList = cafeRequestDto.cafeMenuList + name = name, + address = address, + phoneNumber = phoneNumber, + description = description, + cafeMenuRequestList = cafeMenuList ) } - verify(mockCafeRepository).findByName(cafeRequestDto.name!!) + verify(mockCafeRepository).findByName(name) } @Test @DisplayName("카페 정보 변경 테스트") fun update_cafe_test() { // given - val cafeRequestDto = CafeTestUtils.createCafeRegisterRequest() + val (name, address, phoneNumber, description, cafeMenuList) = CafeTestUtils.createCafeRegisterRequest() val cafe = Cafe.createCafe( - name = cafeRequestDto.name!!, - address = cafeRequestDto.address!!, - phoneNumber = cafeRequestDto.phoneNumber!!, - description = cafeRequestDto.description!!, - cafeMenuRequestList = cafeRequestDto.cafeMenuList + name = name!!, + address = address!!, + phoneNumber = phoneNumber!!, + description = description!!, + cafeMenuRequestList = cafeMenuList ) val cafeId = 50L // TODO findByIdOrNull은 kotlin test 라이브러리 필요한 듯 - `when`(mockCafeRepository.findById(cafeId)).thenReturn(Optional.of(cafe)) + val mockCafe = mock(Cafe::class.java) + `when`(mockCafeRepository.findById(cafeId)).thenReturn(Optional.of(mockCafe)) - // then - mockCafeService.updateInfo( - id = cafeId, - name = "", - address = "", - phoneNumber = "", - description = "", + doNothing().`when`(mockCafe).updateInfo( + name = anyString(), + address = anyString(), + phoneNumber = anyString(), + description = anyString() ) - verify(mockCafeRepository).findById(eq(cafeId)) + // when + mockCafeService.updateInfo( + id = cafeId, + name = "updated_name", + address = "updated_address", + phoneNumber = "updated_phoneNumber", + description = "updated_desc" + ) - // TODO update TEST 방법? + // then + verify(mockCafeRepository).findById(eq(cafeId)) + verify(mockCafe).updateInfo( + name = "updated_name", + address = "updated_address", + phoneNumber = "updated_phoneNumber", + description = "updated_desc" + ) } @Test @@ -131,19 +145,4 @@ internal class CafeServiceTest { ) } } - - 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 - } } \ No newline at end of file diff --git a/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/utils/CafeMenuTestUtils.kt b/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/utils/CafeMenuTestUtils.kt new file mode 100644 index 0000000..7522c2b --- /dev/null +++ b/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/utils/CafeMenuTestUtils.kt @@ -0,0 +1,98 @@ +package io.beaniejoy.dongnecafe.domain.cafe.utils + +import io.beaniejoy.dongnecafe.domain.cafe.entity.CafeMenu +import io.beaniejoy.dongnecafe.domain.cafe.entity.MenuOption +import io.beaniejoy.dongnecafe.domain.cafe.entity.OptionDetail +import io.beaniejoy.dongnecafe.domain.cafe.model.request.CafeMenuRegisterRequest +import io.beaniejoy.dongnecafe.domain.cafe.model.request.MenuOptionRegisterRequest +import io.beaniejoy.dongnecafe.domain.cafe.model.request.OptionDetailRegisterRequest +import org.junit.jupiter.api.Assertions +import java.math.BigDecimal +import javax.persistence.GeneratedValue + +class CafeMenuTestUtils { + companion object { + fun createCafeMenuRegisterRequest(): CafeMenuRegisterRequest { + return createCafeMenuRegisterRequestList()[0] + } + + fun createCafeMenuRegisterRequestList(): List { + val sizeOptionDetailList = listOf( + OptionDetailRegisterRequest(name = "medium", extraPrice = BigDecimal.ZERO), + OptionDetailRegisterRequest(name = "large", extraPrice = BigDecimal.valueOf(200L)), + OptionDetailRegisterRequest(name = "venti", extraPrice = BigDecimal.valueOf(700L)) + ) + val sizeMenuOption = MenuOptionRegisterRequest( + title = "size", + optionDetailList = sizeOptionDetailList + ) + + return listOf( + CafeMenuRegisterRequest( + name = "menu1", + price = BigDecimal.valueOf(2_800L), + menuOptionList = listOf(sizeMenuOption) + ), + CafeMenuRegisterRequest( + name = "menu2", + price = BigDecimal.valueOf(3_500L), + menuOptionList = listOf(sizeMenuOption) + ), + ) + } + + fun assertCafeMenuListEquals( + cafeMenuRequestList: List, + cafeMenuList: List, + ) { + for (index in cafeMenuRequestList.indices) { + Assertions.assertEquals(cafeMenuRequestList[index].name, cafeMenuList[index].name) + Assertions.assertEquals(cafeMenuRequestList[index].price, cafeMenuList[index].price) + + assertMenuOptionListEquals( + cafeMenuRequestList[index].menuOptionList, + cafeMenuList[index].menuOptionList + ) + } + } + + private fun assertMenuOptionListEquals( + menuOptionRequestList: List, + menuOptionList: List, + ) { + for (index in menuOptionRequestList.indices) { + Assertions.assertEquals(menuOptionRequestList[index].title, menuOptionList[index].title) + + assertOptionDetailListEquals( + menuOptionRequestList[index].optionDetailList, + menuOptionList[index].optionDetailList + ) + } + } + + private fun assertOptionDetailListEquals( + optionDetailRequestList: List, + optionDetailList: MutableList, + ) { + for (index in optionDetailRequestList.indices) { + Assertions.assertEquals(optionDetailRequestList[index].name, optionDetailList[index].name) + Assertions.assertEquals(optionDetailRequestList[index].extraPrice, optionDetailList[index].extraPrice) + } + } + + fun injectCafeMenuId( + cafeMenu: CafeMenu, + newCafeMenuId: Long, + ): CafeMenu { + val idField = cafeMenu.javaClass.declaredFields + .find { f -> + f.getAnnotation(GeneratedValue::class.java) != null + } ?: return cafeMenu + + idField.isAccessible = true + idField.set(cafeMenu, newCafeMenuId) + + return cafeMenu + } + } +} \ No newline at end of file diff --git a/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/utils/CafeTestUtils.kt b/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/utils/CafeTestUtils.kt index c01e7e3..66d3d2a 100644 --- a/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/utils/CafeTestUtils.kt +++ b/src/test/java/io/beaniejoy/dongnecafe/domain/cafe/utils/CafeTestUtils.kt @@ -10,6 +10,7 @@ import io.beaniejoy.dongnecafe.domain.cafe.entity.MenuOption import io.beaniejoy.dongnecafe.domain.cafe.entity.OptionDetail import org.junit.jupiter.api.Assertions.* import java.math.BigDecimal +import javax.persistence.GeneratedValue class CafeTestUtils { companion object { @@ -19,46 +20,7 @@ class CafeTestUtils { assertEquals(request.phoneNumber, entity.phoneNumber) assertEquals(request.description, entity.description) - assertCafeMenuListEquals(request.cafeMenuList, entity.cafeMenuList) - } - - private fun assertCafeMenuListEquals( - cafeMenuRequestList: List, - cafeMenuList: List, - ) { - 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, - menuOptionList: List, - ) { - for (index in menuOptionRequestList.indices) { - assertEquals(menuOptionRequestList[index].title, menuOptionList[index].title) - - assertOptionDetailListEquals( - menuOptionRequestList[index].optionDetailList, - menuOptionList[index].optionDetailList - ) - } - } - - private fun assertOptionDetailListEquals( - optionDetailRequestList: List, - optionDetailList: MutableList, - ) { - for (index in optionDetailRequestList.indices) { - assertEquals(optionDetailRequestList[index].name, optionDetailList[index].name) - assertEquals(optionDetailRequestList[index].extraPrice, optionDetailList[index].extraPrice) - } + CafeMenuTestUtils.assertCafeMenuListEquals(request.cafeMenuList, entity.cafeMenuList) } fun createCafeRegisterRequest(): CafeRegisterRequest { @@ -67,36 +29,28 @@ class CafeTestUtils { val phoneNumber = "01012345678" val description = "beanie_cafe_description" - val sizeOptionDetailList = listOf( - OptionDetailRegisterRequest(name = "medium", extraPrice = BigDecimal.ZERO), - OptionDetailRegisterRequest(name = "large", extraPrice = BigDecimal.valueOf(200L)), - OptionDetailRegisterRequest(name = "venti", extraPrice = BigDecimal.valueOf(700L)) - ) - val sizeMenuOption = MenuOptionRegisterRequest( - title = "size", - optionDetailList = sizeOptionDetailList - ) - - val cafeMenuList = listOf( - CafeMenuRegisterRequest( - name = "menu1", - price = BigDecimal.valueOf(2_800L), - menuOptionList = listOf(sizeMenuOption) - ), - CafeMenuRegisterRequest( - name = "menu2", - price = BigDecimal.valueOf(3_500L), - menuOptionList = listOf(sizeMenuOption) - ), - ) - return CafeRegisterRequest( name = cafeName, address = cafeAddress, phoneNumber = phoneNumber, description = description, - cafeMenuList = cafeMenuList + cafeMenuList = CafeMenuTestUtils.createCafeMenuRegisterRequestList() ) } + + 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 + } } } \ No newline at end of file