refactor: ReservationTimeService 테스트 코틀린 전환

This commit is contained in:
이상진 2025-07-18 16:37:15 +09:00
parent a577df0b6a
commit 4e7b850e8d
3 changed files with 85 additions and 84 deletions

View File

@ -28,12 +28,9 @@ class ReservationTimeService(
@Transactional(readOnly = true) @Transactional(readOnly = true)
fun findAllTimes(): ReservationTimesResponse { fun findAllTimes(): ReservationTimesResponse = reservationTimeRepository.findAll()
val response = reservationTimeRepository.findAll() .toResponses()
.map { it.toResponse() }
return ReservationTimesResponse(response)
}
@Transactional @Transactional
fun addTime(reservationTimeRequest: ReservationTimeRequest): ReservationTimeResponse { fun addTime(reservationTimeRequest: ReservationTimeRequest): ReservationTimeResponse {

View File

@ -1,6 +1,7 @@
package roomescape.reservation.web package roomescape.reservation.web
import io.swagger.v3.oas.annotations.media.Schema import io.swagger.v3.oas.annotations.media.Schema
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity
import java.time.LocalTime import java.time.LocalTime
@Schema(name = "예약 시간 저장 요청", description = "예약 시간 저장 요청시 사용됩니다.") @Schema(name = "예약 시간 저장 요청", description = "예약 시간 저장 요청시 사용됩니다.")
@ -38,3 +39,7 @@ data class ReservationTimesResponse(
@field:Schema(description = "모든 시간 목록") @field:Schema(description = "모든 시간 목록")
val times: List<ReservationTimeResponse> val times: List<ReservationTimeResponse>
) )
fun List<ReservationTimeEntity>.toResponses(): ReservationTimesResponse = ReservationTimesResponse(
this.map { it.toResponse() }
)

View File

@ -1,93 +1,92 @@
package roomescape.reservation.business; package roomescape.reservation.business
import static org.assertj.core.api.Assertions.*; import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.mockk
import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpStatus
import roomescape.common.exception.ErrorType
import roomescape.common.exception.RoomescapeException
import roomescape.reservation.infrastructure.persistence.ReservationRepository
import roomescape.reservation.infrastructure.persistence.ReservationTimeRepository
import roomescape.reservation.web.ReservationTimeRequest
import roomescape.util.ReservationTimeFixture
import java.time.LocalTime
import java.time.LocalDateTime; class ReservationTimeServiceTest : FunSpec({
import java.time.LocalTime; val reservationTimeRepository: ReservationTimeRepository = mockk()
val reservationRepository: ReservationRepository = mockk()
import org.junit.jupiter.api.DisplayName; val reservationTimeService = ReservationTimeService(
import org.junit.jupiter.api.Test; reservationTimeRepository = reservationTimeRepository,
import org.springframework.beans.factory.annotation.Autowired; reservationRepository = reservationRepository
import org.springframework.boot.test.context.SpringBootTest; )
import org.springframework.context.annotation.Import;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
import roomescape.common.exception.RoomescapeException; context("findTimeById") {
import roomescape.member.infrastructure.persistence.MemberEntity; test("시간을 찾을 수 없으면 400 에러를 던진다.") {
import roomescape.member.infrastructure.persistence.MemberRepository; val id = 1L
import roomescape.member.infrastructure.persistence.Role;
import roomescape.reservation.infrastructure.persistence.ReservationEntity;
import roomescape.reservation.infrastructure.persistence.ReservationRepository;
import roomescape.reservation.infrastructure.persistence.ReservationStatus;
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity;
import roomescape.reservation.infrastructure.persistence.ReservationTimeRepository;
import roomescape.reservation.web.ReservationTimeRequest;
import roomescape.theme.infrastructure.persistence.ThemeEntity;
import roomescape.theme.infrastructure.persistence.ThemeRepository;
@SpringBootTest // Mocking the behavior of reservationTimeRepository.findByIdOrNull
@Import(ReservationTimeService.class) every { reservationTimeRepository.findByIdOrNull(id) } returns null
@Sql(scripts = "/truncate.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD)
class ReservationTimeServiceTest {
@Autowired shouldThrow<RoomescapeException> {
private ReservationTimeService reservationTimeService; reservationTimeService.findTimeById(id)
@Autowired }.apply {
private ReservationTimeRepository reservationTimeRepository; errorType shouldBe ErrorType.RESERVATION_TIME_NOT_FOUND
@Autowired httpStatus shouldBe HttpStatus.BAD_REQUEST
private ReservationRepository reservationRepository; }
@Autowired }
private ThemeRepository themeRepository; }
@Autowired
private MemberRepository memberRepository;
@Test context("addTime") {
@DisplayName("중복된 예약 시간을 등록하는 경우 예외가 발생한다.") test("중복된 시간이 있으면 409 에러를 던진다.") {
void duplicateTimeFail() { val request = ReservationTimeRequest(startAt = LocalTime.of(10, 0))
// given
reservationTimeRepository.save(new ReservationTimeEntity(null, LocalTime.of(12, 30)));
// when & then // Mocking the behavior of reservationTimeRepository.findByStartAt
assertThatThrownBy(() -> reservationTimeService.addTime(new ReservationTimeRequest(LocalTime.of(12, 30)))) every { reservationTimeRepository.findByStartAt(request.startAt) } returns listOf(mockk())
.isInstanceOf(RoomescapeException.class);
}
@Test shouldThrow<RoomescapeException> {
@DisplayName("존재하지 않는 ID로 시간을 조회하면 예외가 발생한다.") reservationTimeService.addTime(request)
void findTimeByIdFail() { }.apply {
// given errorType shouldBe ErrorType.TIME_DUPLICATED
ReservationTimeEntity saved = reservationTimeRepository.save( httpStatus shouldBe HttpStatus.CONFLICT
new ReservationTimeEntity(null, LocalTime.of(12, 30))); }
}
}
// when context("removeTimeById") {
assert saved.getId() != null; test("시간을 찾을 수 없으면 400 에러를 던진다.") {
long invalidTimeId = saved.getId() + 1; val id = 1L
// when & then // Mocking the behavior of reservationTimeRepository.findByIdOrNull
assertThatThrownBy(() -> reservationTimeService.findTimeById(invalidTimeId)) every { reservationTimeRepository.findByIdOrNull(id) } returns null
.isInstanceOf(RoomescapeException.class);
}
@Test shouldThrow<RoomescapeException> {
@DisplayName("삭제하려는 시간에 예약이 존재하면 예외를 발생한다.") reservationTimeService.removeTimeById(id)
void usingTimeDeleteFail() { }.apply {
// given errorType shouldBe ErrorType.RESERVATION_TIME_NOT_FOUND
LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0); httpStatus shouldBe HttpStatus.BAD_REQUEST
ReservationTimeEntity reservationTime = reservationTimeRepository.save( }
new ReservationTimeEntity(null, localDateTime.toLocalTime())); }
ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "테마명", "설명", "썸네일URL"));
MemberEntity member = memberRepository.save(
new MemberEntity(null, "name", "email@email.com", "password", Role.MEMBER));
// when test("예약이 있는 시간이면 409 에러를 던진다.") {
reservationRepository.save( val id = 1L
new ReservationEntity(null, localDateTime.toLocalDate(), reservationTime, theme, member, val reservationTime = ReservationTimeFixture.create()
ReservationStatus.CONFIRMED));
// then // Mocking the behavior of reservationTimeRepository.findByIdOrNull
assert reservationTime.getId() != null; every { reservationTimeRepository.findByIdOrNull(id) } returns reservationTime
assertThatThrownBy(() -> reservationTimeService.removeTimeById(reservationTime.getId()))
.isInstanceOf(RoomescapeException.class); // Mocking the behavior of reservationRepository.findByReservationTime
} every { reservationRepository.findByReservationTime(reservationTime) } returns listOf(mockk())
}
shouldThrow<RoomescapeException> {
reservationTimeService.removeTimeById(id)
}.apply {
errorType shouldBe ErrorType.TIME_IS_USED_CONFLICT
httpStatus shouldBe HttpStatus.CONFLICT
}
}
}
})