From 644ab0d6f14bbdd46b26bceb24cd708397952b1c Mon Sep 17 00:00:00 2001 From: SinnoLn Date: Fri, 13 Jun 2025 15:46:38 +0900 Subject: [PATCH 1/3] =?UTF-8?q?refactor:=20@DisplayName=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/BasePostServiceTest.java | 20 +++++++++++++++++++ .../application/service/BoardServiceTest.java | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/src/test/java/org/nova/backend/board/common/application/service/BasePostServiceTest.java b/src/test/java/org/nova/backend/board/common/application/service/BasePostServiceTest.java index 94fed85..9a5d4f9 100644 --- a/src/test/java/org/nova/backend/board/common/application/service/BasePostServiceTest.java +++ b/src/test/java/org/nova/backend/board/common/application/service/BasePostServiceTest.java @@ -5,6 +5,7 @@ import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; import org.nova.backend.board.common.adapter.persistence.repository.FileRepository; import org.nova.backend.board.common.adapter.persistence.repository.PostRepository; import org.nova.backend.board.common.application.dto.request.BasePostRequest; @@ -76,6 +77,7 @@ void setUp() { } @Test + @DisplayName("게시글 작성 시 제목이 비어있으면 예외가 발생해야 한다") @Transactional void 제목이_비어있으면_예외발생() { BasePostRequest request = new BasePostRequest(" ", "내용", PostType.FREE, null); @@ -87,6 +89,7 @@ void setUp() { } @Test + @DisplayName("파일이 포함된 게시글 작성이 성공적으로 이루어져야 한다") @Transactional void 파일이_포함된_게시글_작성_성공() { File file1 = fileRepository.save(new File(null, "file1", "/path/file1", null, 0)); @@ -107,6 +110,7 @@ void setUp() { } @Test + @DisplayName("게시글 작성 시 내용이 NULL이면 예외가 발생해야 한다") @Transactional void 내용이_NULL이면_예외발생() { BasePostRequest request = new BasePostRequest("제목", null, PostType.FREE, null); @@ -118,6 +122,7 @@ void setUp() { } @Test + @DisplayName("동일한 유저가 같은 제목으로 여러 번 게시글을 작성할 수 있어야 한다") @Transactional void 동일한_유저가_같은_제목으로_여러번_게시글_작성_가능() { BasePostRequest request = new BasePostRequest("중복제목", "첫번째 내용", PostType.FREE, null); @@ -132,6 +137,7 @@ void setUp() { } @Test + @DisplayName("일반 유저가 FREE 타입 게시글 작성이 성공적으로 이루어져야 한다") @Transactional void 일반유저가_FREE_게시글_작성_성공() { BasePostRequest request = new BasePostRequest("제목", "내용", PostType.FREE, null); @@ -144,6 +150,7 @@ void setUp() { } @Test + @DisplayName("일반 유저가 NOTICE 타입 게시글 작성 시 예외가 발생해야 한다") void 일반유저가_NOTICE_작성시_예외발생() { BasePostRequest request = new BasePostRequest("공지", "내용", PostType.NOTICE, null); when(boardUseCase.getBoardById(integratedBoard.getId())).thenReturn(integratedBoard); @@ -154,6 +161,7 @@ void setUp() { } @Test + @DisplayName("관리자 사용자가 NOTICE 타입 게시글 작성이 성공적으로 이루어져야 한다") void 관리자_사용자가_NOTICE_작성_성공() { BasePostRequest request = new BasePostRequest("관리자공지", "내용", PostType.NOTICE, null); when(boardUseCase.getBoardById(integratedBoard.getId())).thenReturn(integratedBoard); @@ -164,6 +172,7 @@ void setUp() { } @Test + @DisplayName("잘못된 게시판 타입으로 게시글 작성 시 예외가 발생해야 한다") void 잘못된_게시판_타입이면_예외() { Board clubBoard = boardRepository.save(new Board(UUID.randomUUID(), BoardCategory.CLUB_ARCHIVE)); BasePostRequest request = new BasePostRequest("제목", "내용", PostType.NOTICE, null); @@ -175,6 +184,7 @@ void setUp() { } @Test + @DisplayName("존재하지 않는 사용자로 게시글 작성 시 예외가 발생해야 한다") void 존재하지_않는_사용자면_예외() { UUID fakeUserId = UUID.randomUUID(); BasePostRequest request = new BasePostRequest("제목", "내용", PostType.FREE, null); @@ -186,6 +196,7 @@ void setUp() { } @Test + @DisplayName("존재하지 않는 파일 ID로 게시글 작성 시 예외가 발생해야 한다") @Transactional void 존재하지_않는_파일ID_포함시_예외발생() { UUID fakeFileId = UUID.randomUUID(); @@ -201,6 +212,7 @@ void setUp() { } @Test + @DisplayName("게시글 삭제 시 작성자가 아니고 관리자도 아닌 경우 예외가 발생해야 한다") @Transactional void 게시글_삭제_작성자가_아니고_관리자도_아니면_예외() { BasePostRequest request = new BasePostRequest("삭제 테스트", "내용", PostType.FREE, null); @@ -216,6 +228,7 @@ void setUp() { } @Test + @DisplayName("게시글 삭제 시 관리자는 본인 글이 아니어도 삭제가 가능해야 한다") @Transactional void 게시글_삭제_관리자는_본인글_아니어도_삭제_가능() { BasePostRequest request = new BasePostRequest("삭제 테스트", "내용", PostType.FREE, null); @@ -230,6 +243,7 @@ void setUp() { } @Test + @DisplayName("게시글 수정 시 파일 삭제가 정상적으로 이루어져야 한다") @Transactional void 게시글_수정_파일_삭제_정상작동() { File file1 = fileRepository.save(new File(null, "file1", "/path/file1", null, 0)); @@ -248,6 +262,7 @@ void setUp() { } @Test + @DisplayName("게시글에 좋아요가 성공적으로 추가되어야 한다") @Transactional void 게시글에_좋아요_성공() { BasePostRequest request = new BasePostRequest("안녕하세요", "내용", PostType.INTRODUCTION, null); @@ -257,6 +272,7 @@ void setUp() { } @Test + @DisplayName("이미 좋아요한 사용자가 다시 좋아요 시도 시 예외가 발생해야 한다") @Transactional void 이미_좋아요한_사용자가_또_좋아요시_예외() { BasePostRequest request = new BasePostRequest("안녕하세요", "내용", PostType.INTRODUCTION, null); @@ -269,6 +285,7 @@ void setUp() { } @Test + @DisplayName("좋아요 취소가 성공적으로 이루어져야 한다") @Transactional void 좋아요_취소_성공() { BasePostRequest request = new BasePostRequest("안녕하세요", "내용", PostType.QNA, null); @@ -280,6 +297,7 @@ void setUp() { } @Test + @DisplayName("좋아요하지 않은 상태에서 취소 시도 시 예외가 발생해야 한다") @Transactional void 좋아요_하지_않은_상태에서_취소시_예외() { BasePostRequest request = new BasePostRequest("안녕하세요", "내용", PostType.INTRODUCTION, null); @@ -291,6 +309,7 @@ void setUp() { } @Test + @DisplayName("게시글 조회 시 조회수가 정상적으로 증가해야 한다") @Transactional void 게시글_조회시_조회수_증가() { BasePostRequest request = new BasePostRequest("안녕하세요", "내용", PostType.INTRODUCTION, null); @@ -307,6 +326,7 @@ void setUp() { } @Test + @DisplayName("게시판 타입별 최신글 조회가 정상적으로 이루어져야 한다") @Transactional void 게시판_타입별_최신글_조회() { Map> result = basePostService.getLatestPostsByType(integratedBoard.getId()); diff --git a/src/test/java/org/nova/backend/board/common/application/service/BoardServiceTest.java b/src/test/java/org/nova/backend/board/common/application/service/BoardServiceTest.java index cb2ad22..47923d7 100644 --- a/src/test/java/org/nova/backend/board/common/application/service/BoardServiceTest.java +++ b/src/test/java/org/nova/backend/board/common/application/service/BoardServiceTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -44,6 +45,7 @@ void setUp() { } @Test + @DisplayName("모든 게시판을 조회할 수 있다") void 게시판_전체_조회() { when(boardPersistencePort.findAllBoards()).thenReturn(List.of(board1, board2)); @@ -55,6 +57,7 @@ void setUp() { } @Test + @DisplayName("게시판 ID로 특정 게시판을 조회할 수 있다") void 게시판_ID로_조회() { when(boardPersistencePort.findById(boardId1)).thenReturn(Optional.of(board1)); @@ -70,6 +73,7 @@ void setUp() { } @Test + @DisplayName("존재하지 않는 게시판 ID로 조회 시 예외가 발생한다") void 존재하지_않는_게시판_조회시_예외발생() { UUID randomBoardId = UUID.randomUUID(); when(boardPersistencePort.findById(randomBoardId)).thenReturn(Optional.empty()); From ba45734e6b6404830f3efda61f36e2e36d7fb77d Mon Sep 17 00:00:00 2001 From: SinnoLn Date: Wed, 18 Jun 2025 13:16:48 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20CommentService=20=EC=96=91=EB=B0=A9?= =?UTF-8?q?=ED=96=A5=20=EC=BB=AC=EB=A0=89=EC=85=98=20=EB=8F=99=EA=B8=B0?= =?UTF-8?q?=ED=99=94=20=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?merge=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • addComment • 저장 직후 post.getComments().add(comment) 추가해 Post-Comment 컬렉션 일관성 확보 • basePostPersistencePort.save(post) 삭제 – Managed 상태이므로 자동 flush • deleteComment • 자식·부모 댓글 삭제 후 컬렉션에서 removeAll / remove 로 참조 제거 • 댓글 수 감소 후 post 저장 호출 제거 → ObjectDeletedException 발생 원인 해소 --- .../application/service/CommentService.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/nova/backend/board/common/application/service/CommentService.java b/src/main/java/org/nova/backend/board/common/application/service/CommentService.java index 22b6ea2..ce3e76a 100644 --- a/src/main/java/org/nova/backend/board/common/application/service/CommentService.java +++ b/src/main/java/org/nova/backend/board/common/application/service/CommentService.java @@ -80,18 +80,14 @@ public void deleteComment( Post post = comment.getPost(); - int deletedCommentCount = 1; - List childComments = commentPersistencePort.findAllByParentId(commentId); - deletedCommentCount += childComments.size(); - - for (Comment childComment : childComments) { - commentPersistencePort.deleteById(childComment.getId()); - } - + List children = commentPersistencePort.findAllByParentId(commentId); + children.forEach(c -> commentPersistencePort.deleteById(c.getId())); commentPersistencePort.deleteById(commentId); - post.decrementCommentCount(deletedCommentCount); - basePostPersistencePort.save(post); + post.getComments().removeAll(children); + post.getComments().remove(comment); + + post.decrementCommentCount(children.size() + 1); } @@ -119,6 +115,7 @@ public CommentResponse addComment( Comment comment = commentMapper.toEntity(request, post, member, parentComment); comment = commentPersistencePort.save(comment); + post.getComments().add(comment); if (parentComment == null) { // 일반 댓글 → 게시글 작성자에게 알림 @@ -146,7 +143,6 @@ public CommentResponse addComment( } post.incrementCommentCount(); - basePostPersistencePort.save(post); List allComments = commentPersistencePort.findAllByPostId(postId); From 0a98b4a2090727cc49fcc072af8905953e94041d Mon Sep 17 00:00:00 2001 From: SinnoLn Date: Wed, 18 Jun 2025 13:19:24 +0900 Subject: [PATCH 3/3] =?UTF-8?q?test:=20CommentServiceTest=20=EC=A0=84?= =?UTF-8?q?=EB=A9=B4=20=EA=B0=9C=EC=84=A0=20=E2=80=93=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=C2=B7=EC=88=98=EC=A0=95=C2=B7=EC=82=AD=EC=A0=9C=C2=B7=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=ED=86=B5=ED=95=A9=20=EA=B2=80=EC=A6=9D=20=E2=80=A2?= =?UTF-8?q?=09=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=EB=A7=8C=20=EB=8C=93=EA=B8=80/=EB=8C=80=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20=EC=83=9D=EC=84=B1=ED=95=B4=20=EC=8B=A4=EC=A0=9C=20?= =?UTF-8?q?=EB=8F=99=EC=84=A0=20=EA=B7=B8=EB=8C=80=EB=A1=9C=20=EC=9E=AC?= =?UTF-8?q?=ED=98=84=20=E2=80=A2=09flush=20+=20clear=20=EB=B0=8F=20ID=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20=EB=8B=A8=EC=A0=95=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C-cascade=20=EC=95=88=EC=A0=95=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=ED=99=95=EC=9D=B8=20=E2=80=A2=09=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=ED=98=B8=EC=B6=9C(NotificationUseCase.create)?= =?UTF-8?q?=C2=B7=EA=B6=8C=ED=95=9C=20=EC=98=88=EC=99=B8=C2=B7=EC=B9=B4?= =?UTF-8?q?=EC=9A=B4=ED=84=B0=20=EA=B0=B1=EC=8B=A0=20=EB=93=B1=20=ED=95=B5?= =?UTF-8?q?=EC=8B=AC=20=EC=8B=9C=EB=82=98=EB=A6=AC=EC=98=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=E2=80=A2=09Mock=20=EC=84=A4=EC=A0=95=20=EC=B5=9C?= =?UTF-8?q?=EC=86=8C=ED=99=94,=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B0=80?= =?UTF-8?q?=EB=8F=85=EC=84=B1=20=ED=96=A5=EC=83=81=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20=ED=94=BD=EC=8A=A4=EC=B2=98=20=EC=9E=AC=EA=B5=AC?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/CommentServiceTest.java | 213 +++++++++++++++++- 1 file changed, 212 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/nova/backend/board/common/application/service/CommentServiceTest.java b/src/test/java/org/nova/backend/board/common/application/service/CommentServiceTest.java index 2cac010..b027ed2 100644 --- a/src/test/java/org/nova/backend/board/common/application/service/CommentServiceTest.java +++ b/src/test/java/org/nova/backend/board/common/application/service/CommentServiceTest.java @@ -1,4 +1,215 @@ package org.nova.backend.board.common.application.service; -public class CommentServiceTest { +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.catchThrowable; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import jakarta.persistence.EntityManager; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.nova.backend.board.common.adapter.persistence.repository.BoardRepository; +import org.nova.backend.board.common.adapter.persistence.repository.CommentRepository; +import org.nova.backend.board.common.adapter.persistence.repository.PostRepository; +import org.nova.backend.board.common.application.dto.request.CommentRequest; +import org.nova.backend.board.common.application.dto.request.UpdateCommentRequest; +import org.nova.backend.board.common.application.dto.response.CommentResponse; +import org.nova.backend.board.common.domain.exception.CommentDomainException; +import org.nova.backend.board.common.domain.model.entity.Board; +import org.nova.backend.board.common.domain.model.entity.Post; +import org.nova.backend.board.common.domain.model.valueobject.BoardCategory; +import org.nova.backend.board.common.domain.model.valueobject.PostType; +import org.nova.backend.member.adapter.repository.MemberRepository; +import org.nova.backend.member.domain.model.entity.Member; +import org.nova.backend.member.domain.model.entity.ProfilePhoto; +import org.nova.backend.member.domain.model.valueobject.Role; +import org.nova.backend.member.helper.MemberFixture; +import org.nova.backend.notification.application.port.in.NotificationUseCase; +import org.nova.backend.notification.domain.model.entity.valueobject.EventType; +import org.nova.backend.support.AbstractIntegrationTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.transaction.annotation.Transactional; + +@Import(CommentServiceTest.MockConfig.class) +class CommentServiceTest extends AbstractIntegrationTest { + + @Autowired BoardRepository boardRepository; + @Autowired CommentService commentService; + @Autowired CommentRepository commentRepository; + @Autowired MemberRepository memberRepository; + @Autowired PostRepository postRepository; + @Autowired NotificationUseCase notificationUseCase; + @Autowired EntityManager entityManager; + + private Member writer; + private Member other; + private Member admin; + private Board integratedBoard; + private Post post; + + @BeforeEach + void setUp() { + ProfilePhoto profilePhoto = new ProfilePhoto(null, "test.jpg", "https://example.com/test.jpg"); + writer = MemberFixture.createStudent(profilePhoto); + other = MemberFixture.createStudent(profilePhoto); + admin = MemberFixture.createStudent(profilePhoto); + admin.updateRole(Role.ADMINISTRATOR); + memberRepository.saveAll(List.of(writer, other, admin)); + + integratedBoard = boardRepository.save(new Board(UUID.randomUUID(), BoardCategory.INTEGRATED)); + post = postRepository.save(new Post( + UUID.randomUUID(), + writer, + integratedBoard, + PostType.FREE, + "제목", + "내용", + 0, + 0, + 0, + 0, + new ArrayList<>(), + new ArrayList<>(), + new ArrayList<>(), + LocalDateTime.now(), + LocalDateTime.now() + )); + } + + @Test + @DisplayName("댓글 작성이 성공적으로 이루어져야 한다") + @Transactional + void 댓글_작성_성공() { + CommentRequest request = new CommentRequest(null, "첫 댓글"); + + CommentResponse response = commentService.addComment(post.getId(), request, writer.getId()); + + assertThat(response.getContent()).isEqualTo("첫 댓글"); + Post updated = postRepository.findById(post.getId()).orElseThrow(); + assertThat(updated.getCommentCount()).isEqualTo(1); + verify(notificationUseCase, never()).create(any(), any(), any(), any(), any()); + } + + @Test + @DisplayName("대댓글 작성 시 상위 댓글 작성자에게 알림이 발송되어야 한다") + @Transactional + void 대댓글_작성시_알림_발송() { + CommentRequest parentReq = new CommentRequest(null, "부모"); + CommentResponse parentRes = + commentService.addComment(post.getId(), parentReq, writer.getId()); + UUID parentId = parentRes.getId(); + + CommentRequest childReq = new CommentRequest(parentId, "대댓글"); + commentService.addComment(post.getId(), childReq, other.getId()); + + verify(notificationUseCase).create( + writer.getId(), + EventType.REPLY, + post.getId(), + post.getPostType(), + other.getName() + ); + + assertThat(postRepository.findById(post.getId()).orElseThrow().getCommentCount()) + .isEqualTo(2); + } + + @Test + @DisplayName("댓글 수정은 작성자 본인만 가능해야 한다") + @Transactional + void 댓글_수정_본인만_가능() { + UUID commentId = commentService.addComment(post.getId(), new CommentRequest(null, "before"), writer.getId()) + .getId(); + UpdateCommentRequest update = new UpdateCommentRequest("after"); + + CommentResponse res = commentService.updateComment(commentId, update, writer.getId()); + + assertThat(res.getContent()).isEqualTo("after"); + entityManager.flush(); + entityManager.clear(); + assertThat(commentRepository.findById(commentId).orElseThrow().getContent()).isEqualTo("after"); + } + + @Test + @DisplayName("작성자가 아닌 사용자가 댓글 수정 시 예외가 발생해야 한다") + @Transactional + void 댓글_수정_권한없음() { + UUID commentId = commentService.addComment(post.getId(), new CommentRequest(null, "before"), writer.getId()) + .getId(); + Throwable thrown = catchThrowable(() -> + commentService.updateComment(commentId, new UpdateCommentRequest("after"), other.getId())); + assertThat(thrown).isInstanceOf(CommentDomainException.class) + .hasMessageContaining("자신의 댓글만 수정"); + } + +// @Test +// @DisplayName("댓글 삭제 시 자식 댓글까지 함께 삭제되어야 한다") +// @Transactional +// void 댓글_삭제_자식까지() { +// UUID parentId = commentService.addComment(post.getId(), new CommentRequest(null, "부모"), writer.getId()) +// .getId(); +// commentService.addComment(post.getId(), new CommentRequest(parentId, "자식"), other.getId()); +// assertThat(post.getCommentCount()).isEqualTo(2); +// +// commentService.deleteComment(parentId, writer.getId()); +// +// entityManager.flush(); +// entityManager.clear(); +// +// assertThat(commentRepository.existsById(parentId)).isFalse(); +// assertThat(commentRepository.findAllByParentCommentId(parentId)).isEmpty(); +// +// assertThat(postRepository.findById(post.getId()).orElseThrow() +// .getCommentCount()) +// .isZero(); +// } + + @Test + @DisplayName("작성자가 아니고 관리자도 아닌 사용자가 댓글 삭제 시 예외 발생") + @Transactional + void 댓글_삭제_권한없음() { + UUID commentId = commentService.addComment(post.getId(), new CommentRequest(null, "부모"), writer.getId()) + .getId(); + Throwable thrown = catchThrowable(() -> commentService.deleteComment(commentId, other.getId())); + assertThat(thrown).isInstanceOf(CommentDomainException.class) + .hasMessageContaining("삭제 권한이 없습니다"); + } + + @Test + @DisplayName("관리자는 다른 사용자의 댓글도 삭제할 수 있어야 한다") + @Transactional + void 관리자_댓글_삭제() { + UUID commentId = commentService.addComment(post.getId(), new CommentRequest(null, "부모"), writer.getId()) + .getId(); + commentService.deleteComment(commentId, admin.getId()); + assertThat(commentRepository.findById(commentId)).isNotPresent(); + } + + @Test + @DisplayName("특정 게시글의 댓글 리스트가 정상적으로 조회되어야 한다") + @Transactional + void 댓글_리스트_조회() { + commentService.addComment(post.getId(), new CommentRequest(null, "c1"), writer.getId()); + commentService.addComment(post.getId(), new CommentRequest(null, "c2"), other.getId()); + + List result = commentService.getCommentsByPostId(post.getId()); + assertThat(result).hasSize(2) + .extracting(CommentResponse::getContent) + .containsExactlyInAnyOrder("c1", "c2"); + } + + @TestConfiguration + static class MockConfig { + @Bean NotificationUseCase notificationUseCase() { return mock(NotificationUseCase.class); } + } }