Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ group = "gomushin"
version = "0.0.1-SNAPSHOT"

val mockkVersion = "1.13.10"
val kotestVersion = "5.5.4"

java {
toolchain {
Expand Down Expand Up @@ -90,6 +91,8 @@ dependencies {
testImplementation("org.mockito.kotlin:mockito-kotlin:5.0.0")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testImplementation("io.mockk:mockk:${mockkVersion}")
testImplementation("io.kotest:kotest-runner-junit5:$kotestVersion")
testImplementation("io.kotest:kotest-assertions-core:$kotestVersion")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.springframework.stereotype.Component

@Component
object SpringContextHolder : ApplicationContextAware {
private lateinit var context: ApplicationContext
lateinit var context: ApplicationContext

override fun setApplicationContext(applicationContext: ApplicationContext) {
context = applicationContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class CommentService(
}

@Transactional(readOnly = true)
fun findAllByLetter(letter: Letter): List<Comment> {
return commentRepository.findAllByLetterId(letter.id)
fun findAllByLetterId(id: Long): List<Comment> {
return commentRepository.findAllByLetterId(id)
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class ReadLetterFacade(
PictureResponse.of(picture)
}

val comments = commentService.findAllByLetter(letter)
val comments = commentService.findAllByLetterId(letter.id)
val commentResponses = comments.map { comment ->
CommentResponse.of(comment)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package gomushin.backend.schedule.domain.facade

import gomushin.backend.core.CustomUserDetails
import gomushin.backend.core.common.support.SpringContextHolder
import gomushin.backend.core.configuration.env.AppEnv
import gomushin.backend.core.infrastructure.exception.BadRequestException
import gomushin.backend.member.domain.entity.Member
import gomushin.backend.member.domain.service.MemberService
import gomushin.backend.schedule.domain.entity.Comment
Expand All @@ -9,90 +12,165 @@ import gomushin.backend.schedule.domain.service.CommentService
import gomushin.backend.schedule.domain.service.LetterService
import gomushin.backend.schedule.dto.request.UpsertCommentRequest
import gomushin.backend.schedule.facade.UpsertAndDeleteCommentFacade
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.impl.annotations.InjectMockKs
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.junit.jupiter.MockitoExtension
import org.springframework.context.ApplicationContext
import kotlin.test.Test

@ExtendWith(MockitoExtension::class)
@ExtendWith(MockKExtension::class)
class UpsertAndDeleteCommentFacadeTest {

@Mock
@MockK
private lateinit var commentService: CommentService

@Mock
@MockK
private lateinit var letterService: LetterService

@Mock
@MockK
private lateinit var memberService: MemberService

@InjectMocks
@MockK(relaxed = true)
private lateinit var mockAppEnv: AppEnv

@MockK
private lateinit var mockApplicationContext: ApplicationContext

@InjectMockKs
private lateinit var upsertAndDeleteCommentFacade: UpsertAndDeleteCommentFacade

@BeforeEach
fun setup() {
SpringContextHolder.context = mockApplicationContext
every { mockApplicationContext.getBean(AppEnv::class.java) } returns mockAppEnv
every { mockAppEnv.getId() } returns "test-env"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 원래 코드는 yml의 spring.application.name에 있는 값을 가져오는 걸로 알고있는데 지금 test yml보니깐 거기는 "backend"로 되어있던데 "test-env"로 한 이유가 궁금해

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 test yml 에 있는거 까먹고 그냥 하드코딩했어
사실 이건 중요한거 아니고, lazyInit 예외를 막으려고 넣은거라..

}


@DisplayName("댓글 생성 또는 수정 성공")
@Test
fun upsert_success() {
// given
val customUserDetails = mock(CustomUserDetails::class.java)
val customUserDetails = mockk<CustomUserDetails>()
val letterId = 1L
val memberId = 1L
val upsertCommentRequest = UpsertCommentRequest(
commentId = null,
content = "댓글 내용"
)

val mockMember = mock(Member::class.java).apply {
`when`(id).thenReturn(memberId)
`when`(nickname).thenReturn("테스트유저")
}
val mockLetter = mock(Letter::class.java).apply {
`when`(id).thenReturn(letterId)
}

`when`(customUserDetails.getId()).thenReturn(memberId)
`when`(memberService.getById(memberId)).thenReturn(mockMember)
`when`(letterService.getById(letterId)).thenReturn(mockLetter)
val upsertCommentRequest = mockk<UpsertCommentRequest>()
val member = mockk<Member>()
val letter = mockk<Letter>()
every { customUserDetails.getId() } returns 1L
every { memberService.getById(any()) } returns member
every { letterService.getById(any()) } returns letter
every { upsertCommentRequest.commentId } returns 1L
every { letter.id } returns letterId
every { member.id } returns 1L
every { member.nickname } returns "닉네임"
every { commentService.upsert(any(), any(), any(), any(), any()) } returns Unit

// when
upsertAndDeleteCommentFacade.upsert(customUserDetails, letterId, upsertCommentRequest)

// then
verify(commentService, times(1)).upsert(
id = upsertCommentRequest.commentId,
letterId = mockLetter.id,
authorId = mockMember.id,
nickname = "테스트유저",
upsertCommentRequest = upsertCommentRequest
)
verify { memberService.getById(1L) }
verify { letterService.getById(1L) }
verify { commentService.upsert(1L, 1L, 1L, "닉네임", upsertCommentRequest) }
}

@DisplayName("댓글 삭제 성공")
@Test
fun delete_success() {
// given
val customUserDetails = mock(CustomUserDetails::class.java)
val letterId = 1L
val commentId = 1L
val mockMember = mock(Member::class.java)
val mockComment = mock(Comment::class.java)

// when
`when`(customUserDetails.getId()).thenReturn(1L)
`when`(memberService.getById(customUserDetails.getId())).thenReturn(mockMember)
`when`(commentService.getById(commentId)).thenReturn(mockComment)
`when`(mockMember.id).thenReturn(1L)
`when`(mockComment.authorId).thenReturn(1L)
`when`(mockComment.letterId).thenReturn(letterId)
`when`(mockComment.letterId).thenReturn(letterId)
@Nested
inner class DeleteTest {
@DisplayName("댓글 삭제 성공")
@Test
fun delete_success() {
// given
val customUserDetails = mockk<CustomUserDetails>()
val letterId = 1L
val memberId = 1L
val commentId = 1L
val authorId = 1L
val member = mockk<Member>()
val comment = mockk<Comment>()

upsertAndDeleteCommentFacade.delete(customUserDetails, letterId, commentId)
every { customUserDetails.getId() } returns 1L
every { memberService.getById(any()) } returns member
every { commentService.getById(commentId) } returns comment
every { comment.authorId } returns authorId
every { member.id } returns memberId
every { comment.letterId } returns letterId
every { commentService.delete(commentId) } returns Unit

// then
verify(memberService, times(1)).getById(customUserDetails.getId())
verify(commentService, times(1)).getById(commentId)
verify(commentService, times(1)).delete(commentId)
// when
upsertAndDeleteCommentFacade.delete(customUserDetails, letterId, commentId)

// then
verify { memberService.getById(1L) }
verify { commentService.getById(commentId) }
verify { commentService.delete(commentId) }
}

@DisplayName("댓글 삭제 시 comment 작성자 ID와 Member ID가 다를 경우, 에러 발생")
@Test
fun delete_shouldThrowBadRequestException_When_Comment_AuthorId_is_Not_MemberId() {
// given
val customUserDetails = mockk<CustomUserDetails>()
val letterId = 1L
val memberId = 2L
val commentId = 1L
val authorId = 1L
val member = mockk<Member>()
val comment = mockk<Comment>()

every { customUserDetails.getId() } returns 1L
every { memberService.getById(any()) } returns member
every { commentService.getById(commentId) } returns comment
every { comment.authorId } returns authorId
every { member.id } returns memberId
every { comment.letterId } returns letterId
every { commentService.delete(commentId) } returns Unit

// when
val exception = shouldThrow<BadRequestException> {
upsertAndDeleteCommentFacade.delete(customUserDetails, letterId, commentId)
}

// then
exception.error.element.message.resolved shouldBe "댓글은 작성자만 삭제하거나 업데이트 할 수 있어요."
}

@DisplayName("댓글의 letterId와 입력으로 받은 letterId가 다른 경우, 예외를 발생시킨다.")
@Test
fun delete_shouldThrowBadRequestException_when_commentLetterId_and_letterId_not_match() {
// given
val customUserDetails = mockk<CustomUserDetails>()
val letterId = 1L
val memberId = 1L
val commentId = 1L
val authorId = 1L
val member = mockk<Member>()
val comment = mockk<Comment>()

every { customUserDetails.getId() } returns 1L
every { memberService.getById(any()) } returns member
every { commentService.getById(commentId) } returns comment
every { comment.authorId } returns authorId
every { member.id } returns memberId
every { comment.letterId } returns 2L
every { commentService.delete(commentId) } returns Unit

// when
val exception = shouldThrow<BadRequestException> {
upsertAndDeleteCommentFacade.delete(customUserDetails, letterId, commentId)
}

// then
exception.error.element.message.resolved shouldBe "편지에 해당하는 댓글이 아니에요."
}
}
}
Loading