[#30] 코드 구조 개선 #31

Merged
pricelees merged 31 commits from refactor/#30 into main 2025-08-06 10:16:08 +00:00
6 changed files with 425 additions and 93 deletions
Showing only changes of commit ea047d38bb - Show all commits

View File

@ -16,4 +16,7 @@ class TimeWithAvailability(
startAt = startAt, startAt = startAt,
isAvailable = isReservable isAvailable = isReservable
) )
// for test
fun canReserve(): Boolean = isReservable
} }

View File

@ -1,38 +1,42 @@
package roomescape.time.business package roomescape.time.business
import io.kotest.assertions.assertSoftly
import io.kotest.assertions.throwables.shouldNotThrow import io.kotest.assertions.throwables.shouldNotThrow
import io.kotest.assertions.throwables.shouldThrow import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.mockk.Runs import io.mockk.Runs
import io.mockk.every import io.mockk.every
import io.mockk.just import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import org.springframework.data.repository.findByIdOrNull import roomescape.theme.business.domain.TimeWithAvailability
import roomescape.reservation.infrastructure.persistence.ReservationRepository import roomescape.theme.exception.ThemeErrorCode
import roomescape.theme.exception.ThemeException
import roomescape.time.exception.TimeErrorCode import roomescape.time.exception.TimeErrorCode
import roomescape.time.exception.TimeException import roomescape.time.exception.TimeException
import roomescape.time.infrastructure.persistence.TimeRepository import roomescape.time.implement.TimeFinder
import roomescape.time.implement.TimeWriter
import roomescape.time.infrastructure.persistence.TimeEntity
import roomescape.time.web.TimeCreateRequest import roomescape.time.web.TimeCreateRequest
import roomescape.util.TsidFactory
import roomescape.util.TimeFixture import roomescape.util.TimeFixture
import java.time.LocalDate
import java.time.LocalTime import java.time.LocalTime
class TimeServiceTest : FunSpec({ class TimeServiceTest : FunSpec({
val timeRepository: TimeRepository = mockk() val timeFinder: TimeFinder = mockk()
val reservationRepository: ReservationRepository = mockk() val timeWriter: TimeWriter = mockk()
val timeService = TimeService( val timeService = TimeService(timeFinder, timeWriter)
tsidFactory = TsidFactory,
timeRepository = timeRepository,
reservationRepository = reservationRepository
)
context("findTimeById") { context("findById") {
test("시간을 찾을 수 없으면 예외 응답") { test("시간을 찾을 수 없으면 예외 응답") {
val id = 1L val id = 1L
every { timeRepository.findByIdOrNull(id) } returns null every {
timeFinder.findById(id)
} throws TimeException(TimeErrorCode.TIME_NOT_FOUND)
shouldThrow<TimeException> { shouldThrow<TimeException> {
timeService.findById(id) timeService.findById(id)
@ -42,22 +46,81 @@ class TimeServiceTest : FunSpec({
} }
} }
context("findTimes") {
test("정상 응답") {
val times: List<TimeEntity> = listOf(
TimeFixture.create(startAt = LocalTime.now()),
TimeFixture.create(startAt = LocalTime.now().plusMinutes(1)),
TimeFixture.create(startAt = LocalTime.now().plusMinutes(2))
)
every {
timeFinder.findAll()
} returns times
val response = timeService.findTimes()
assertSoftly(response.times) {
it shouldHaveSize times.size
it.map { time -> time.startAt } shouldContainExactly times.map { time -> time.startAt }
}
}
}
context("findTimesWithAvailability") {
val date = LocalDate.now()
val themeId = 1L
test("정상 응답") {
val times: List<TimeWithAvailability> = listOf(
TimeWithAvailability(1, LocalTime.now(), date, themeId, true),
TimeWithAvailability(2, LocalTime.now().plusMinutes(1), date, themeId, false),
TimeWithAvailability(3, LocalTime.now().plusMinutes(2), date, themeId, true)
)
every {
timeFinder.findAllWithAvailabilityByDateAndThemeId(date, themeId)
} returns times
val response = timeService.findTimesWithAvailability(date, themeId)
assertSoftly(response.times) {
it shouldHaveSize times.size
it.map { time -> time.isAvailable } shouldContainExactly times.map { time -> time.canReserve() }
}
}
test("테마를 찾을 수 없으면 예외 응답") {
every {
timeFinder.findAllWithAvailabilityByDateAndThemeId(date, themeId)
} throws ThemeException(ThemeErrorCode.THEME_NOT_FOUND)
shouldThrow<ThemeException> {
timeService.findTimesWithAvailability(date, themeId)
}.also {
it.errorCode shouldBe ThemeErrorCode.THEME_NOT_FOUND
}
}
}
context("createTime") { context("createTime") {
val request = TimeCreateRequest(startAt = LocalTime.of(10, 0)) val request = TimeCreateRequest(startAt = LocalTime.of(10, 0))
test("정상 저장") { test("정상 저장") {
every { timeRepository.existsByStartAt(request.startAt) } returns false val time: TimeEntity = TimeFixture.create(startAt = request.startAt)
every { timeRepository.save(any()) } returns TimeFixture.create(
id = 1L, every {
startAt = request.startAt timeWriter.create(request.startAt)
) } returns time
val response = timeService.createTime(request) val response = timeService.createTime(request)
response.id shouldBe 1L response.id shouldBe time.id
} }
test("중복된 시간이 있으면 예외 응답") { test("중복된 시간이 있으면 예외 응답") {
every { timeRepository.existsByStartAt(request.startAt) } returns true every {
timeWriter.create(request.startAt)
} throws TimeException(TimeErrorCode.TIME_DUPLICATED)
shouldThrow<TimeException> { shouldThrow<TimeException> {
timeService.createTime(request) timeService.createTime(request)
@ -67,14 +130,13 @@ class TimeServiceTest : FunSpec({
} }
} }
context("removeTimeById") { context("deleteTime") {
test("정상 제거 및 응답") { test("정상 제거 및 응답") {
val id = 1L val id = 1L
val time = TimeFixture.create(id = id) val time = TimeFixture.create(id = id)
every { timeRepository.findByIdOrNull(id) } returns time every { timeFinder.findById(id) } returns time
every { reservationRepository.findAllByTime(time) } returns emptyList() every { timeWriter.delete(time) } just Runs
every { timeRepository.delete(time) } just Runs
shouldNotThrow<Exception> { shouldNotThrow<Exception> {
timeService.deleteTime(id) timeService.deleteTime(id)
@ -84,7 +146,7 @@ class TimeServiceTest : FunSpec({
test("시간을 찾을 수 없으면 예외 응답") { test("시간을 찾을 수 없으면 예외 응답") {
val id = 1L val id = 1L
every { timeRepository.findByIdOrNull(id) } returns null every { timeFinder.findById(id) } throws TimeException(TimeErrorCode.TIME_NOT_FOUND)
shouldThrow<TimeException> { shouldThrow<TimeException> {
timeService.deleteTime(id) timeService.deleteTime(id)
@ -97,9 +159,8 @@ class TimeServiceTest : FunSpec({
val id = 1L val id = 1L
val time = TimeFixture.create() val time = TimeFixture.create()
every { timeRepository.findByIdOrNull(id) } returns time every { timeFinder.findById(id) } returns time
every { timeWriter.delete(time) } throws TimeException(TimeErrorCode.TIME_ALREADY_RESERVED)
every { reservationRepository.findAllByTime(time) } returns listOf(mockk())
shouldThrow<TimeException> { shouldThrow<TimeException> {
timeService.deleteTime(id) timeService.deleteTime(id)

View File

@ -0,0 +1,109 @@
package roomescape.time.implement
import io.kotest.assertions.assertSoftly
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.springframework.data.repository.findByIdOrNull
import roomescape.reservation.implement.ReservationFinder
import roomescape.theme.business.domain.TimeWithAvailability
import roomescape.theme.exception.ThemeErrorCode
import roomescape.theme.exception.ThemeException
import roomescape.theme.implement.ThemeFinder
import roomescape.time.exception.TimeErrorCode
import roomescape.time.exception.TimeException
import roomescape.time.infrastructure.persistence.TimeRepository
import roomescape.util.TimeFixture
import java.time.LocalDate
import java.time.LocalTime
class TimeFinderTest : FunSpec({
val timeRepository: TimeRepository = mockk()
val reservationFinder: ReservationFinder = mockk()
val themeFinder: ThemeFinder = mockk()
val timeFinder = TimeFinder(timeRepository, reservationFinder, themeFinder)
context("findAll") {
test("모든 시간을 조회한다.") {
every {
timeRepository.findAll()
} returns listOf(mockk(), mockk(), mockk())
timeRepository.findAll() shouldHaveSize 3
}
}
context("findById") {
val timeId = 1L
test("동일한 ID인 시간을 찾아 응답한다.") {
every {
timeRepository.findByIdOrNull(timeId)
} returns mockk()
timeFinder.findById(timeId)
verify(exactly = 1) {
timeRepository.findByIdOrNull(timeId)
}
}
test("동일한 ID인 시간이 없으면 실패한다.") {
every {
timeRepository.findByIdOrNull(timeId)
} returns null
shouldThrow<TimeException> {
timeFinder.findById(timeId)
}.also {
it.errorCode shouldBe TimeErrorCode.TIME_NOT_FOUND
}
}
}
context("findAllWithAvailabilityByDateAndThemeId") {
val date = LocalDate.now()
val themeId = 1L
test("테마를 찾을 수 없으면 실패한다.") {
every {
themeFinder.findById(themeId)
} throws ThemeException(ThemeErrorCode.THEME_NOT_FOUND)
shouldThrow<ThemeException> {
timeFinder.findAllWithAvailabilityByDateAndThemeId(date, themeId)
}.also {
it.errorCode shouldBe ThemeErrorCode.THEME_NOT_FOUND
}
}
test("날짜, 테마에 맞는 예약 자체가 없으면 모든 시간이 예약 가능하다.") {
every {
themeFinder.findById(themeId)
} returns mockk()
every {
reservationFinder.findAllByDateAndTheme(date, any())
} returns emptyList()
every {
timeRepository.findAll()
} returns listOf(
TimeFixture.create(startAt = LocalTime.now()),
TimeFixture.create(startAt = LocalTime.now().plusMinutes(30))
)
val result: List<TimeWithAvailability> =
timeFinder.findAllWithAvailabilityByDateAndThemeId(date, themeId)
assertSoftly(result) {
it shouldHaveSize 2
it.all { time -> time.canReserve() }
}
}
}
})

View File

@ -0,0 +1,74 @@
package roomescape.time.implement
import io.kotest.assertions.throwables.shouldNotThrow
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 roomescape.reservation.implement.ReservationFinder
import roomescape.time.exception.TimeErrorCode
import roomescape.time.exception.TimeException
import roomescape.time.infrastructure.persistence.TimeEntity
import roomescape.time.infrastructure.persistence.TimeRepository
import roomescape.util.TimeFixture
import java.time.LocalTime
class TimeValidatorTest : FunSpec({
val timeRepository: TimeRepository = mockk()
val reservationFinder: ReservationFinder = mockk()
val timeValidator = TimeValidator(timeRepository, reservationFinder)
context("validateIsAlreadyExists") {
val startAt = LocalTime.now()
test("같은 이메일을 가진 회원이 있으면 예외를 던진다.") {
every {
timeRepository.existsByStartAt(startAt)
} returns true
shouldThrow<TimeException> {
timeValidator.validateIsAlreadyExists(startAt)
}.also {
it.errorCode shouldBe TimeErrorCode.TIME_DUPLICATED
}
}
test("같은 이메일을 가진 회원이 없으면 종료한다.") {
every {
timeRepository.existsByStartAt(startAt)
} returns false
shouldNotThrow<TimeException> {
timeValidator.validateIsAlreadyExists(startAt)
}
}
}
context("validateIsReserved") {
val time: TimeEntity = TimeFixture.create(startAt = LocalTime.now())
test("해당 시간에 예약이 있으면 예외를 던진다.") {
every {
reservationFinder.isTimeReserved(time)
} returns true
shouldThrow<TimeException> {
timeValidator.validateIsReserved(time)
}.also {
it.errorCode shouldBe TimeErrorCode.TIME_ALREADY_RESERVED
}
}
test("해당 시간에 예약이 없으면 종료한다.") {
every {
reservationFinder.isTimeReserved(time)
} returns false
shouldNotThrow<TimeException> {
timeValidator.validateIsReserved(time)
}
}
}
})

View File

@ -0,0 +1,84 @@
package roomescape.time.implement
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.mockk.*
import roomescape.time.exception.TimeErrorCode
import roomescape.time.exception.TimeException
import roomescape.time.infrastructure.persistence.TimeEntity
import roomescape.time.infrastructure.persistence.TimeRepository
import roomescape.util.TimeFixture
import roomescape.util.TsidFactory
import java.time.LocalTime
class TimeWriterTest : FunSpec({
val timeValidator: TimeValidator = mockk()
val timeRepository: TimeRepository = mockk()
val timeWriter = TimeWriter(timeValidator, timeRepository, TsidFactory)
context("create") {
val startAt = LocalTime.now()
test("중복된 시간이 있으면 실패한다.") {
every {
timeValidator.validateIsAlreadyExists(startAt)
} throws TimeException(TimeErrorCode.TIME_DUPLICATED)
shouldThrow<TimeException> {
timeWriter.create(startAt)
}.also {
it.errorCode shouldBe TimeErrorCode.TIME_DUPLICATED
}
}
test("중복된 시간이 없으면 저장한다.") {
every {
timeValidator.validateIsAlreadyExists(startAt)
} just Runs
every {
timeRepository.save(any())
} returns TimeFixture.create(startAt = startAt)
timeWriter.create(startAt)
verify(exactly = 1) {
timeRepository.save(any())
}
}
}
context("delete") {
val time: TimeEntity = TimeFixture.create()
test("예약이 있는 시간이면 실패한다.") {
every {
timeValidator.validateIsReserved(time)
} throws TimeException(TimeErrorCode.TIME_ALREADY_RESERVED)
shouldThrow<TimeException> {
timeWriter.delete(time)
}.also {
it.errorCode shouldBe TimeErrorCode.TIME_ALREADY_RESERVED
}
}
test("예약이 없는 시간이면 제거한다.") {
every {
timeValidator.validateIsReserved(time)
} just Runs
every {
timeRepository.delete(time)
} just Runs
timeWriter.delete(time)
verify(exactly = 1) {
timeRepository.delete(time)
}
}
}
})

View File

@ -1,28 +1,21 @@
package roomescape.time.web package roomescape.time.web
import com.ninjasquad.springmockk.MockkBean import com.ninjasquad.springmockk.MockkBean
import com.ninjasquad.springmockk.SpykBean
import io.kotest.assertions.assertSoftly import io.kotest.assertions.assertSoftly
import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.mockk.every import io.mockk.every
import org.hamcrest.Matchers.equalTo import org.hamcrest.Matchers.equalTo
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.context.annotation.Import
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext
import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.MockMvc
import roomescape.auth.exception.AuthErrorCode import roomescape.auth.exception.AuthErrorCode
import roomescape.common.config.JacksonConfig import roomescape.theme.exception.ThemeErrorCode
import roomescape.reservation.infrastructure.persistence.ReservationRepository import roomescape.theme.exception.ThemeException
import roomescape.time.business.TimeService import roomescape.time.business.TimeService
import roomescape.time.exception.TimeErrorCode import roomescape.time.exception.TimeErrorCode
import roomescape.time.infrastructure.persistence.TimeEntity import roomescape.time.exception.TimeException
import roomescape.time.infrastructure.persistence.TimeRepository
import roomescape.util.ReservationFixture
import roomescape.util.RoomescapeApiTest import roomescape.util.RoomescapeApiTest
import roomescape.util.ThemeFixture
import roomescape.util.TimeFixture import roomescape.util.TimeFixture
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalTime import java.time.LocalTime
@ -32,15 +25,9 @@ class TimeControllerTest(
val mockMvc: MockMvc, val mockMvc: MockMvc,
) : RoomescapeApiTest() { ) : RoomescapeApiTest() {
@SpykBean @MockkBean
private lateinit var timeService: TimeService private lateinit var timeService: TimeService
@MockkBean
private lateinit var timeRepository: TimeRepository
@MockkBean
private lateinit var reservationRepository: ReservationRepository
init { init {
Given("등록된 모든 시간을 조회할 때") { Given("등록된 모든 시간을 조회할 때") {
val endpoint = "/times" val endpoint = "/times"
@ -52,11 +39,11 @@ class TimeControllerTest(
Then("정상 응답") { Then("정상 응답") {
every { every {
timeRepository.findAll() timeService.findTimes()
} returns listOf( } returns listOf(
TimeFixture.create(id = 1L), TimeFixture.create(id = 1L),
TimeFixture.create(id = 2L) TimeFixture.create(id = 2L)
) ).toResponse()
runGetTest( runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
@ -76,7 +63,7 @@ class TimeControllerTest(
loginAsUser() loginAsUser()
val expectedError = AuthErrorCode.ACCESS_DENIED val expectedError = AuthErrorCode.ACCESS_DENIED
Then("에러 응답을 받는다.") { Then("예외 응답") {
runGetTest( runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
@ -85,7 +72,7 @@ class TimeControllerTest(
}.andExpect { }.andExpect {
content { content {
contentType(MediaType.APPLICATION_JSON) contentType(MediaType.APPLICATION_JSON)
jsonPath("$.code") { value(expectedError.errorCode) } jsonPath("$.code") { equalTo(expectedError.errorCode) }
} }
} }
} }
@ -139,8 +126,8 @@ class TimeControllerTest(
Then("동일한 시간이 존재하면 예외 응답") { Then("동일한 시간이 존재하면 예외 응답") {
val expectedError = TimeErrorCode.TIME_DUPLICATED val expectedError = TimeErrorCode.TIME_DUPLICATED
every { every {
timeRepository.existsByStartAt(time) timeService.createTime(request)
} returns true } throws TimeException(expectedError)
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
@ -150,7 +137,7 @@ class TimeControllerTest(
status { isEqualTo(expectedError.httpStatus.value()) } status { isEqualTo(expectedError.httpStatus.value()) }
content { content {
contentType(MediaType.APPLICATION_JSON) contentType(MediaType.APPLICATION_JSON)
jsonPath("$.code") { value(expectedError.errorCode) } jsonPath("$.code") { equalTo(expectedError.errorCode) }
} }
} }
} }
@ -159,7 +146,7 @@ class TimeControllerTest(
When("관리자가 아닌 경우") { When("관리자가 아닌 경우") {
loginAsUser() loginAsUser()
Then("에러 응답을 받는다.") { Then("예외 응답") {
val expectedError = AuthErrorCode.ACCESS_DENIED val expectedError = AuthErrorCode.ACCESS_DENIED
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
@ -197,9 +184,10 @@ class TimeControllerTest(
Then("없는 시간을 조회하면 예외 응답") { Then("없는 시간을 조회하면 예외 응답") {
val id = 1L val id = 1L
val expectedError = TimeErrorCode.TIME_NOT_FOUND val expectedError = TimeErrorCode.TIME_NOT_FOUND
every { every {
timeRepository.findByIdOrNull(id) timeService.deleteTime(id)
} returns null } throws TimeException(expectedError)
runDeleteTest( runDeleteTest(
mockMvc = mockMvc, mockMvc = mockMvc,
@ -208,7 +196,7 @@ class TimeControllerTest(
status { isEqualTo(expectedError.httpStatus.value()) } status { isEqualTo(expectedError.httpStatus.value()) }
content { content {
contentType(MediaType.APPLICATION_JSON) contentType(MediaType.APPLICATION_JSON)
jsonPath("$.code") { value(expectedError.errorCode) } jsonPath("$.code") { equalTo(expectedError.errorCode) }
} }
} }
} }
@ -216,13 +204,10 @@ class TimeControllerTest(
Then("예약이 있는 시간을 삭제하면 예외 응답") { Then("예약이 있는 시간을 삭제하면 예외 응답") {
val id = 1L val id = 1L
val expectedError = TimeErrorCode.TIME_ALREADY_RESERVED val expectedError = TimeErrorCode.TIME_ALREADY_RESERVED
every {
timeRepository.findByIdOrNull(id)
} returns TimeFixture.create(id = id)
every { every {
reservationRepository.findAllByTime(any()) timeService.deleteTime(id)
} returns listOf(ReservationFixture.create()) } throws TimeException(expectedError)
runDeleteTest( runDeleteTest(
mockMvc = mockMvc, mockMvc = mockMvc,
@ -231,7 +216,7 @@ class TimeControllerTest(
status { isEqualTo(expectedError.httpStatus.value()) } status { isEqualTo(expectedError.httpStatus.value()) }
content { content {
contentType(MediaType.APPLICATION_JSON) contentType(MediaType.APPLICATION_JSON)
jsonPath("$.code") { value(expectedError.errorCode) } jsonPath("$.code") { equalTo(expectedError.errorCode) }
} }
} }
} }
@ -240,7 +225,7 @@ class TimeControllerTest(
When("관리자가 아닌 경우") { When("관리자가 아닌 경우") {
loginAsUser() loginAsUser()
Then("에러 응답을 받는다.") { Then("예외 응답") {
val expectedError = AuthErrorCode.ACCESS_DENIED val expectedError = AuthErrorCode.ACCESS_DENIED
runDeleteTest( runDeleteTest(
mockMvc = mockMvc, mockMvc = mockMvc,
@ -254,35 +239,34 @@ class TimeControllerTest(
} }
Given("날짜, 테마가 주어졌을 때") { Given("날짜, 테마가 주어졌을 때") {
loginAsUser() beforeTest {
loginAsUser()
}
val date: LocalDate = LocalDate.now() val date: LocalDate = LocalDate.now()
val themeId = 1L val themeId = 1L
When("저장된 예약 시간이 있으면") { When("테마를 찾을 수 있으면") {
val times: List<TimeEntity> = listOf( Then("해당 날짜와 테마에 대한 예약 여부가 담긴 모든 시간을 응답") {
TimeFixture.create(id = 1L, startAt = LocalTime.of(10, 0)), val response = TimeWithAvailabilityListResponse(
TimeFixture.create(id = 2L, startAt = LocalTime.of(11, 0)) listOf(
) TimeWithAvailabilityResponse(id = 1L, startAt = LocalTime.now(), isAvailable = true),
TimeWithAvailabilityResponse(
every { id = 2L,
timeRepository.findAll() startAt = LocalTime.now().plusMinutes(30),
} returns times isAvailable = false
),
Then("그 시간과, 해당 날짜와 테마에 대한 예약 여부가 담긴 목록을 응답") { TimeWithAvailabilityResponse(
id = 1L,
every { startAt = LocalTime.now().plusHours(1),
reservationRepository.findByDateAndThemeId(date, themeId) isAvailable = true
} returns listOf( ),
ReservationFixture.create(
id = 1L,
date = date,
theme = ThemeFixture.create(id = themeId),
time = times[0]
) )
) )
every {
timeService.findTimesWithAvailability(date, themeId)
} returns response
val response = runGetTest( val result = runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = "/times/search?date=$date&themeId=$themeId", endpoint = "/times/search?date=$date&themeId=$themeId",
) { ) {
@ -292,13 +276,30 @@ class TimeControllerTest(
} }
}.andReturn().readValue(TimeWithAvailabilityListResponse::class.java) }.andReturn().readValue(TimeWithAvailabilityListResponse::class.java)
assertSoftly(response.times) { assertSoftly(result.times) {
this shouldHaveSize times.size this shouldHaveSize response.times.size
this[0].id shouldBe times[0].id this[0].id shouldBe response.times[0].id
this[0].isAvailable shouldBe false this[0].isAvailable shouldBe response.times[0].isAvailable
this[1].id shouldBe times[1].id this[1].id shouldBe response.times[1].id
this[1].isAvailable shouldBe true this[1].isAvailable shouldBe response.times[1].isAvailable
}
}
}
When("테마를 찾을 수 없으면") {
val expectedError = ThemeErrorCode.THEME_NOT_FOUND
every {
timeService.findTimesWithAvailability(date, themeId)
} throws ThemeException(expectedError)
Then("예외 응답") {
runGetTest(
mockMvc = mockMvc,
endpoint = "/times/search?date=$date&themeId=$themeId",
) {
status { isNotFound() }
jsonPath("$.code", equalTo(expectedError.errorCode))
} }
} }
} }