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

Merged
pricelees merged 31 commits from refactor/#30 into main 2025-08-06 10:16:08 +00:00
3 changed files with 81 additions and 96 deletions
Showing only changes of commit 866c9a368f - Show all commits

View File

@ -1,25 +1,24 @@
package roomescape.auth.web
import com.ninjasquad.springmockk.SpykBean
import com.ninjasquad.springmockk.MockkBean
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import org.hamcrest.Matchers.equalTo
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.web.servlet.MockMvc
import roomescape.auth.business.AuthService
import roomescape.auth.exception.AuthErrorCode
import roomescape.auth.exception.AuthException
import roomescape.common.exception.CommonErrorCode
import roomescape.common.exception.ErrorCode
import roomescape.member.exception.MemberErrorCode
import roomescape.member.exception.MemberException
import roomescape.util.MemberFixture
import roomescape.util.RoomescapeApiTest
@WebMvcTest(controllers = [AuthController::class])
class AuthControllerTest(
val mockMvc: MockMvc
) : RoomescapeApiTest() {
class AuthControllerTest(val mockMvc: MockMvc) : RoomescapeApiTest() {
@SpykBean
@MockkBean
private lateinit var authService: AuthService
val userRequest: LoginRequest = MemberFixture.userLoginRequest()
@ -32,12 +31,8 @@ class AuthControllerTest(
val expectedToken = "expectedToken"
every {
memberFinder.findByEmailAndPassword(userRequest.email, userRequest.password)
} returns user
every {
jwtHandler.createToken(user.id!!)
} returns expectedToken
authService.login(userRequest)
} returns LoginResponse(expectedToken)
Then("토큰을 반환한다.") {
runPostTest(
@ -52,12 +47,13 @@ class AuthControllerTest(
}
When("회원을 찾지 못하면") {
val expectedError = AuthErrorCode.LOGIN_FAILED
every {
memberFinder.findByEmailAndPassword(userRequest.email, userRequest.password)
} throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND)
authService.login(userRequest)
} throws AuthException(expectedError)
Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.LOGIN_FAILED
runPostTest(
mockMvc = mockMvc,
endpoint = endpoint,
@ -68,6 +64,7 @@ class AuthControllerTest(
}
}
}
When("입력 값이 잘못되면") {
val expectedErrorCode: ErrorCode = CommonErrorCode.INVALID_INPUT_VALUE
@ -97,6 +94,10 @@ class AuthControllerTest(
loginAsUser()
Then("회원의 이름과 권한을 응답한다") {
every {
authService.checkLogin(user.id!!)
} returns LoginCheckResponse(user.name, user.role.name)
runGetTest(
mockMvc = mockMvc,
endpoint = endpoint,
@ -110,14 +111,12 @@ class AuthControllerTest(
When("토큰은 있지만 회원을 찾을 수 없으면") {
val invalidMemberId: Long = -1L
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
every { jwtHandler.getMemberIdFromToken(any()) } returns invalidMemberId
every {
memberFinder.findById(invalidMemberId)
} throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND)
every { authService.checkLogin(invalidMemberId) } throws AuthException(expectedError)
Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
runGetTest(
mockMvc = mockMvc,
endpoint = endpoint,
@ -132,13 +131,11 @@ class AuthControllerTest(
val endpoint = "/logout"
When("토큰으로 memberId 조회가 가능하면") {
every {
jwtHandler.getMemberIdFromToken(any())
} returns 1L
loginAsUser()
every {
memberFinder.findById(1L)
} returns MemberFixture.create(id = 1L)
authService.logout(user.id!!)
} just Runs
Then("정상 응답한다.") {
runPostTest(

View File

@ -1,68 +1,53 @@
package roomescape.member.controller
import com.ninjasquad.springmockk.MockkBean
import com.ninjasquad.springmockk.SpykBean
import io.kotest.assertions.assertSoftly
import io.kotest.matchers.collections.shouldContainAll
import io.kotest.matchers.shouldBe
import io.mockk.every
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.web.servlet.MockMvc
import roomescape.auth.exception.AuthErrorCode
import roomescape.member.business.MemberService
import roomescape.member.exception.MemberErrorCode
import roomescape.member.exception.MemberException
import roomescape.member.implement.MemberWriter
import roomescape.member.infrastructure.persistence.Role
import roomescape.member.web.MemberController
import roomescape.member.web.MemberRetrieveListResponse
import roomescape.member.web.SignupRequest
import roomescape.member.web.*
import roomescape.util.MemberFixture
import roomescape.util.RoomescapeApiTest
import kotlin.random.Random
@WebMvcTest(controllers = [MemberController::class])
class MemberControllerTest(
@Autowired private val mockMvc: MockMvc
) : RoomescapeApiTest() {
@SpykBean
private lateinit var memberService: MemberService
class MemberControllerTest(val mockMvc: MockMvc) : RoomescapeApiTest() {
@MockkBean
private lateinit var memberWriter: MemberWriter
private lateinit var memberService: MemberService
init {
given("GET /members 요청을") {
val endpoint = "/members"
every { memberFinder.findAll() } returns listOf(
val response = listOf(
MemberFixture.create(id = Random.nextLong(), name = "name1"),
MemberFixture.create(id = Random.nextLong(), name = "name2"),
MemberFixture.create(id = Random.nextLong(), name = "name3"),
)
).toRetrieveListResponse()
every { memberService.findMembers() } returns response
`when`("관리자가 보내면") {
loginAsAdmin()
then("성공한다.") {
val result: String = runGetTest(
val result: MemberRetrieveListResponse = runGetTest(
mockMvc = mockMvc,
endpoint = endpoint,
) {
status { isOk() }
}.andReturn().response.contentAsString
}.andReturn().readValue(MemberRetrieveListResponse::class.java)
val response: MemberRetrieveListResponse = readValue(
responseJson = result,
valueType = MemberRetrieveListResponse::class.java
)
assertSoftly(response.members) {
it.size shouldBe 3
it.map { m -> m.name } shouldContainAll listOf("name1", "name2", "name3")
assertSoftly(result.members) {
it.size shouldBe response.members.size
it.map { m -> m.name } shouldContainAll response.members.map { m -> m.name }
}
}
}
@ -107,14 +92,14 @@ class MemberControllerTest(
)
`when`("같은 이메일이 없으면") {
every {
memberWriter.create(any(), any(), any(), any())
memberService.createMember(request)
} returns MemberFixture.create(
id = 1,
name = request.name,
account = request.email,
password = request.password,
role = Role.MEMBER
)
).toSignupResponse()
then("id과 이름을 담아 성공 응답") {
runPostTest(
@ -130,13 +115,12 @@ class MemberControllerTest(
}
`when`("같은 이메일이 있으면") {
val expectedError = MemberErrorCode.DUPLICATE_EMAIL
every {
memberWriter.create(request.name, request.email, request.password, Role.MEMBER)
} throws MemberException(MemberErrorCode.DUPLICATE_EMAIL)
memberService.createMember(request)
} throws MemberException(expectedError)
then("에러 응답") {
val expectedError = MemberErrorCode.DUPLICATE_EMAIL
runPostTest(
mockMvc = mockMvc,
endpoint = endpoint,

View File

@ -21,18 +21,15 @@ import java.time.LocalDate
import java.time.LocalTime
@WebMvcTest(TimeController::class)
class TimeControllerTest(
val mockMvc: MockMvc,
) : RoomescapeApiTest() {
class TimeControllerTest(val mockMvc: MockMvc) : RoomescapeApiTest() {
@MockkBean
private lateinit var timeService: TimeService
init {
Given("등록된 모든 시간을 조회할 때") {
Given("GET /times 요청을") {
val endpoint = "/times"
When("관리자인 경우") {
When("관리자가 보내면") {
beforeTest {
loginAsAdmin()
}
@ -59,7 +56,7 @@ class TimeControllerTest(
}
}
When("관리자가 아닌 경우") {
When("관리자가 보내지 않았다면") {
loginAsUser()
val expectedError = AuthErrorCode.ACCESS_DENIED
@ -79,10 +76,10 @@ class TimeControllerTest(
}
}
Given("시간을 추가할 때") {
Given("POST /times 요청을") {
val endpoint = "/times"
When("관리자인 경우") {
When("관리자가 보낼 때") {
beforeTest {
loginAsAdmin()
}
@ -143,7 +140,7 @@ class TimeControllerTest(
}
}
When("관리자가 아닌 경우") {
When("관리자가 보내지 않았다면") {
loginAsUser()
Then("예외 응답") {
@ -160,10 +157,10 @@ class TimeControllerTest(
}
}
Given("시간을 삭제할 때") {
Given("DELETE /times/{id} 요청을") {
val endpoint = "/times/1"
When("관리자인 경우") {
When("관리자가 보낼 때") {
beforeTest {
loginAsAdmin()
}
@ -222,7 +219,7 @@ class TimeControllerTest(
}
}
When("관리자가 아닌 경우") {
When("관리자가 보내지 않았다면") {
loginAsUser()
Then("예외 응답") {
@ -238,28 +235,21 @@ class TimeControllerTest(
}
}
Given("날짜, 테마가 주어졌을 때") {
beforeTest {
loginAsUser()
}
Given("GET /times/search?date={date}&themeId={themeId} 요청을 ") {
val date: LocalDate = LocalDate.now()
val themeId = 1L
When("테마를 찾을 수 있으면") {
Then("해당 날짜와 테마에 대한 예약 여부가 담긴 모든 시간을 응답") {
When("회원이 보낼 때") {
beforeTest {
loginAsUser()
}
Then("정상 응답") {
val response = TimeWithAvailabilityListResponse(
listOf(
TimeWithAvailabilityResponse(id = 1L, startAt = LocalTime.now(), isAvailable = true),
TimeWithAvailabilityResponse(
id = 2L,
startAt = LocalTime.now().plusMinutes(30),
isAvailable = false
),
TimeWithAvailabilityResponse(
id = 1L,
startAt = LocalTime.now().plusHours(1),
isAvailable = true
),
TimeWithAvailabilityResponse(1L, LocalTime.of(10, 0), true),
TimeWithAvailabilityResponse(2L, LocalTime.of(10, 1), false),
TimeWithAvailabilityResponse(3L, LocalTime.of(10, 2), true)
)
)
every {
@ -285,15 +275,12 @@ class TimeControllerTest(
this[1].isAvailable shouldBe response.times[1].isAvailable
}
}
}
Then("테마를 찾을 수 없으면 예외 응답") {
val expectedError = ThemeErrorCode.THEME_NOT_FOUND
every {
timeService.findTimesWithAvailability(date, themeId)
} throws ThemeException(expectedError)
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",
@ -303,6 +290,23 @@ class TimeControllerTest(
}
}
}
When("비회원이 보내면") {
doNotLogin()
Then("예외 응답") {
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
runGetTest(
mockMvc = mockMvc,
endpoint = "/times/search?date=$date&themeId=$themeId",
) {
status { isEqualTo(expectedError.httpStatus.value()) }
jsonPath("$.code", equalTo(expectedError.errorCode))
}
}
}
}
}
}