[#9] API 테스트를 @SpringbootTest에서 @WebMvcTest 기반으로 전환 #10

Merged
pricelees merged 6 commits from refactor/#9 into main 2025-07-15 07:21:17 +00:00
Showing only changes of commit 6f87780344 - Show all commits

View File

@ -1,23 +1,34 @@
package roomescape.auth.web package roomescape.auth.web
import com.ninjasquad.springmockk.SpykBean
import io.mockk.every import io.mockk.every
import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.containsString
import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.equalTo
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.data.repository.findByIdOrNull import org.springframework.data.repository.findByIdOrNull
import org.springframework.test.web.servlet.MockMvc
import roomescape.auth.service.AuthService
import roomescape.common.exception.ErrorType import roomescape.common.exception.ErrorType
import roomescape.util.MemberFixture import roomescape.util.MemberFixture
import roomescape.util.RoomescapeApiTest import roomescape.util.RoomescapeApiTest
class AuthControllerTest : RoomescapeApiTest() { @WebMvcTest(controllers = [AuthController::class])
class AuthControllerTest(
@Autowired mockMvc: MockMvc
) : RoomescapeApiTest() {
@SpykBean
private lateinit var authService: AuthService
val userRequest: LoginRequest = MemberFixture.userLoginRequest() val userRequest: LoginRequest = MemberFixture.userLoginRequest()
init { init {
Given("로그인 요청을 보낼 때") { Given("로그인 요청을 보낼 때") {
val endpoint: String = "/login" val endpoint = "/login"
When("로그인에 성공하면") { When("로그인에 성공하면") {
val expectedToken: String = "expectedToken" val expectedToken = "expectedToken"
every { every {
memberRepository.findByEmailAndPassword(userRequest.email, userRequest.password) memberRepository.findByEmailAndPassword(userRequest.email, userRequest.password)
@ -28,12 +39,19 @@ class AuthControllerTest : RoomescapeApiTest() {
} returns expectedToken } returns expectedToken
Then("토큰을 쿠키에 담아 응답한다") { Then("토큰을 쿠키에 담아 응답한다") {
runPostTest(endpoint, body = MemberFixture.userLoginRequest()) { runPostTest(
statusCode(200) mockMvc = mockMvc,
cookie("accessToken", expectedToken) endpoint = endpoint,
header("Set-Cookie", containsString("Max-Age=1800")) body = userRequest,
header("Set-Cookie", containsString("HttpOnly")) log = true
header("Set-Cookie", containsString("Secure")) ) {
status { isOk() }
header {
string("Set-Cookie", containsString("accessToken=$expectedToken"))
string("Set-Cookie", containsString("Max-Age=1800"))
string("Set-Cookie", containsString("HttpOnly"))
string("Set-Cookie", containsString("Secure"))
}
} }
} }
} }
@ -44,10 +62,14 @@ class AuthControllerTest : RoomescapeApiTest() {
} returns null } returns null
Then("400 에러를 응답한다") { Then("400 에러를 응답한다") {
runPostTest(endpoint, body = userRequest) { runPostTest(
log().all() mockMvc = mockMvc,
statusCode(400) endpoint = endpoint,
body("errorType", `is`(ErrorType.MEMBER_NOT_FOUND.name)) body = userRequest,
log = true
) {
status { isBadRequest() }
jsonPath("$.errorType", equalTo(ErrorType.MEMBER_NOT_FOUND.name))
} }
} }
} }
@ -57,36 +79,47 @@ class AuthControllerTest : RoomescapeApiTest() {
Then("이메일 형식이 잘못된 경우") { Then("이메일 형식이 잘못된 경우") {
val invalidRequest: LoginRequest = userRequest.copy(email = "invalid") val invalidRequest: LoginRequest = userRequest.copy(email = "invalid")
runPostTest(endpoint, body = invalidRequest) { runPostTest(
log().all() mockMvc = mockMvc,
statusCode(400) endpoint = endpoint,
body("message", `is`("이메일 형식이 일치하지 않습니다. 예시: abc123@gmail.com")) body = invalidRequest,
log = true
) {
status { isBadRequest() }
jsonPath("$.message", containsString("이메일 형식이 일치하지 않습니다."))
} }
} }
Then("비밀번호가 공백인 경우") { Then("비밀번호가 공백인 경우") {
val invalidRequest = userRequest.copy(password = " ") val invalidRequest = userRequest.copy(password = " ")
runPostTest(endpoint, body = invalidRequest) { runPostTest(
log().all() mockMvc = mockMvc,
statusCode(400) endpoint = endpoint,
body("message", `is`("비밀번호는 공백일 수 없습니다.")) body = invalidRequest,
log = true
) {
status { isBadRequest() }
jsonPath("$.message", containsString("비밀번호는 공백일 수 없습니다."))
} }
} }
} }
} }
Given("로그인 상태를 확인할 때") { Given("로그인 상태를 확인할 때") {
val endpoint: String = "/login/check" val endpoint = "/login/check"
When("로그인된 회원의 ID로 요청하면") { When("로그인된 회원의 ID로 요청하면") {
every { jwtHandler.getMemberIdFromToken(any()) } returns user.id!! loginAsUser()
every { memberRepository.findByIdOrNull(user.id!!) } returns user
Then("회원의 이름을 응답한다") { Then("회원의 이름을 응답한다") {
runGetTest(endpoint) { runGetTest(
statusCode(200) mockMvc = mockMvc,
body("data.name", `is`(user.name)) endpoint = endpoint,
log = true
) {
status { isOk() }
jsonPath("$.data.name", equalTo(user.name))
} }
} }
} }
@ -98,39 +131,55 @@ class AuthControllerTest : RoomescapeApiTest() {
every { memberRepository.findByIdOrNull(invalidMemberId) } returns null every { memberRepository.findByIdOrNull(invalidMemberId) } returns null
Then("400 에러를 응답한다.") { Then("400 에러를 응답한다.") {
runGetTest(endpoint) { runGetTest(
statusCode(400) mockMvc = mockMvc,
body("errorType", `is`(ErrorType.MEMBER_NOT_FOUND.name)) endpoint = endpoint,
log = true
) {
status { isBadRequest() }
jsonPath("$.errorType", equalTo(ErrorType.MEMBER_NOT_FOUND.name))
} }
} }
} }
} }
Given("로그아웃 요청을 보낼 때") { Given("로그아웃 요청을 보낼 때") {
val endpoint: String = "/logout" val endpoint = "/logout"
When("로그인 상태가 아니라면") { When("로그인 상태가 아니라면") {
setUpNotLoggedIn() doNotLogin()
Then("로그인 페이지로 이동한다.") { Then("로그인 페이지로 이동한다.") {
runPostTest(endpoint) { runPostTest(
log().all() mockMvc = mockMvc,
statusCode(302) endpoint = endpoint,
header("Location", containsString("/login")) log = true
) {
status { is3xxRedirection() }
header {
string("Location", "/login")
}
} }
} }
} }
When("로그인 상태라면") { When("로그인 상태라면") {
setUpUser() loginAsUser()
every { memberRepository.findByIdOrNull(user.id!!) } returns user
Then("토큰의 존재 여부와 무관하게 토큰을 만료시킨다.") { Then("토큰의 존재 여부와 무관하게 토큰을 만료시킨다.") {
runPostTest(endpoint) { runPostTest(
log().all() mockMvc = mockMvc,
statusCode(200) endpoint = endpoint,
cookie("accessToken", "") log = true
header("Set-Cookie", containsString("Max-Age=0")) ) {
status { isOk() }
header {
string("Set-Cookie", containsString("Max-Age=0"))
string("Set-Cookie", containsString("accessToken="))
string("Set-Cookie", containsString("Path=/"))
string("Set-Cookie", containsString("HttpOnly"))
string("Set-Cookie", containsString("Secure"))
}
} }
} }
} }