서비스 레이어(비즈니스 레이어만)
커버리지 (라인 + 브랜치) 70%
테스트 템플릿
public class FeedbackServiceTest extends IntegrationTest {
private final String feedbackMessage = "이번 스프린트에서 아주 잘해주셨습니다.";
@Autowired private FeedbackService feedbackService;
@Autowired private FeedbackRepository feedbackRepository;
@Autowired private SprintRepository sprintRepository;
@Autowired private MemberRepository memberRepository;
@Autowired private TeamRepository teamRepository;
@Autowired private TeamParticipantRepository teamParticipantRepository;
@Autowired private ProjectRepository projectRepository;
@Autowired private ProjectParticipantRepository projectParticipantRepository;
private ProjectParticipant sender;
private ProjectParticipant anotherSender;
private ProjectParticipant receiver;
private ProjectParticipant anotherReceiver;
private ProjectParticipant unknownReceiver;
private Sprint sprint;
private Sprint anotherSprint;
private Project project;
private Project anotherProject;
@BeforeEach
void setUp() {
Member senderMember =
memberRepository.save(
Member.createMember(
"testSenderNickname",
"testSenderProfileImageUrl",
OauthInfo.createOauthInfo("testOauthId", "testOauthProvider")));
Member receiverMember =
memberRepository.save(
Member.createMember(
"testReceiverNickname",
"testReceiverProfileImageUrl",
OauthInfo.createOauthInfo("testOauthId", "testOauthProvider")));
// 초기에 로그인한 사용자를 sender로 설정
setAuthenticatedUser(senderMember);
Team team = teamRepository.save(Team.createTeam("testName", "testDescription"));
TeamParticipant teamParticipantAdmin =
teamParticipantRepository.save(
TeamParticipant.createParticipant(
senderMember, team, TeamParticipantRole.ADMIN));
TeamParticipant teamParticipantUser =
teamParticipantRepository.save(
TeamParticipant.createParticipant(
receiverMember, team, TeamParticipantRole.USER));
project =
projectRepository.save(
Project.createProject(
team, "testTitle", "testDescription", LocalDate.of(2030, 1, 1)));
anotherProject =
projectRepository.save(
Project.createProject(
team, "testTitle", "testDescription", LocalDate.of(2030, 1, 1)));
sender =
projectParticipantRepository.save(
ProjectParticipant.createProjectParticipant(
teamParticipantAdmin,
project,
senderMember.getNickname(),
senderMember.getProfileImageUrl(),
ProjectParticipantRole.ADMIN));
receiver =
projectParticipantRepository.save(
ProjectParticipant.createProjectParticipant(
teamParticipantUser,
project,
receiverMember.getNickname(),
receiverMember.getProfileImageUrl(),
ProjectParticipantRole.MEMBER));
anotherReceiver =
projectParticipantRepository.save(
ProjectParticipant.createProjectParticipant(
teamParticipantUser,
anotherProject,
"test",
"test",
ProjectParticipantRole.ADMIN));
anotherSender =
projectParticipantRepository.save(
ProjectParticipant.createProjectParticipant(
teamParticipantUser,
anotherProject,
"test",
"test",
ProjectParticipantRole.ADMIN));
unknownReceiver =
projectParticipantRepository.save(
ProjectParticipant.createProjectParticipant(
teamParticipantUser,
anotherProject,
"UNKNOWN_PROJECT_NICKNAME",
"UNKNOWN_PROJECT_PROFILE_URL",
ProjectParticipantRole.MEMBER));
sprint =
sprintRepository.save(
Sprint.createSprint(project, "testSprint", "testGoal", LocalDate.now()));
anotherSprint =
sprintRepository.save(
Sprint.createSprint(
anotherProject,
"testAnotherSprint",
"testAnotherGoal",
LocalDate.now()));
}
@Nested
class 피드백_메시지를_전송할_때 {
@Test
@Transactional
void 입력_값이_정상이라면_피드백_메시지_전송에_성공한다() {
// given
FeedbackSendRequest request =
new FeedbackSendRequest(sprint.getId(), receiver.getId(), feedbackMessage);
// when
feedbackService.sendFeedback(request);
// then
Feedback feedback = feedbackRepository.findById(1L).get();
assertThat(feedback.getSender()).isEqualTo(sender);
assertThat(feedback.getReceiver()).isEqualTo(receiver);
assertThat(feedback.getSprint()).isEqualTo(sprint);
assertThat(feedback.getMessage()).isEqualTo(feedbackMessage);
}
@Test
void 본인에게_피드백_메시지를_전송하면_예외가_발생한다() {
// given
FeedbackSendRequest request =
new FeedbackSendRequest(sprint.getId(), sender.getId(), feedbackMessage);
// when & then
assertThatThrownBy(() -> feedbackService.sendFeedback(request))
.isInstanceOf(CommonException.class)
.hasMessage(FeedbackErrorCode.CANNOT_SEND_FEEDBACK_TO_SELF.getMessage());
}
@Test
void 같은_스프린트에서_특정_대상에게_피드백_메시지를_두_번_전송하면_예외가_발생한다() {
// given
FeedbackSendRequest request =
new FeedbackSendRequest(sprint.getId(), receiver.getId(), feedbackMessage);
feedbackService.sendFeedback(request);
// when & then
FeedbackSendRequest duplicateRequest =
new FeedbackSendRequest(sprint.getId(), receiver.getId(), feedbackMessage);
assertThatThrownBy(() -> feedbackService.sendFeedback(duplicateRequest))
.isInstanceOf(CommonException.class)
.hasMessage(FeedbackErrorCode.FEEDBACK_ALREADY_SENT.getMessage());
}
@Test
void 스프린트가_존재하지_않는다면_예외가_발생한다() {
// given
FeedbackSendRequest request =
new FeedbackSendRequest(999L, receiver.getId(), feedbackMessage);
// when & then
assertThatThrownBy(() -> feedbackService.sendFeedback(request))
.isInstanceOf(CommonException.class)
.hasMessage(SprintErrorCode.SPRINT_NOT_FOUND.getMessage());
}
@Test
void 피드백을_받을_대상이_존재하지_않는다면_예외가_발생한다() {
// given
FeedbackSendRequest request =
new FeedbackSendRequest(sprint.getId(), 999L, feedbackMessage);
// when & then
assertThatThrownBy(() -> feedbackService.sendFeedback(request))
.isInstanceOf(CommonException.class)
.hasMessage(FeedbackErrorCode.RECEIVER_NOT_FOUND.getMessage());
}
@Test
void 같은_프로젝트에_속하지_않은_대상에게_피드백_메시지를_전송하면_예외가_발생한다() {
// given
FeedbackSendRequest request =
new FeedbackSendRequest(
sprint.getId(), anotherReceiver.getId(), feedbackMessage);
// when & then
assertThatThrownBy(() -> feedbackService.sendFeedback(request))
.isInstanceOf(CommonException.class)
.hasMessage(FeedbackErrorCode.INVALID_PROJECT_PARTICIPANT.getMessage());
}
private void setAuthenticatedUser(Member member) {
UserDetails userDetails = new PrincipalDetails(member.getId(), member.getRole());
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(token);
}
class WineServiceImplTest extends IntegrationTestSupport {
@Autowired
WineRepository wineRepository;
@Autowired
WineService wineService;
@Autowired
MemberRepository memberRepository;
@Autowired
TastingNoteRepository tastingNoteRepository;
@Autowired
WineWishlistRepository wineWishlistRepository;
@DisplayName("와인 이름을 받아서 이름을 포함하는 모든 와인을 조회한다.")
@Test
void searchWineByName() {
// given
Wine wine1 = createWine("대중적인 레드 와인 10년");
Wine wine2 = createWine("대중적인 화이트 와인 13년");
Wine wine3 = createWine("대중적인 화이트 스파클링 와인 20년");
Wine wine4 = createWine("매니아들이 찾는 레드 와인 30년");
wineRepository.saveAll(List.of(wine1, wine2, wine3, wine4));
Pageable pageable = PageRequest.of(0, 10);
// when
PageResponse<WinePreviewResponse> winePreviewResponsePageResponse1 = wineService.searchWinesByName("0년", pageable);
PageResponse<WinePreviewResponse> winePreviewResponsePageResponse2 = wineService.searchWinesByName("대중적", pageable);
// then
assertWinePreviewPageResponse(winePreviewResponsePageResponse1, 0, 1,
List.of(WinePreviewResponse.of(wine1), WinePreviewResponse.of(wine3), WinePreviewResponse.of(wine4)));
assertWinePreviewPageResponse(winePreviewResponsePageResponse2, 0, 1,
List.of(WinePreviewResponse.of(wine1), WinePreviewResponse.of(wine3), WinePreviewResponse.of(wine2)));
}
class WineRepositoryImplTest extends IntegrationTestSupport {
@Autowired
WineRepository wineRepository;
@Autowired
MemberRepository memberRepository;
@Autowired
WineWishlistRepository wineWishlistRepository;
@DisplayName("멤버 선호 와인 종류, 지역, 가격대에 따른 와인 추천 리스트 조회")
@Test
void findRecommendWinesByMemberPreferSortAndAreaAndPrice() {
// given
Wine wine1 = createWine("와인1", "레드", "프랑스", 10000, "피노누아", 4.5f);
Wine wine2 = createWine("와인2", "화이트", "이탈리아", 20000, "샤르도네", 4.0f);
Wine wine3 = createWine("와인3", "로제", "스페인", 30000, "피노누아", 3.5f);
Wine wine4 = createWine("와인4", "레드", "프랑스", 40000, "피노누아", 4.5f);
Wine wine5 = createWine("와인5", "화이트", "이탈리아", 50000, "샤르도네", 4.0f);
Wine wine6 = createWine("와인6", "로제", "스페인", 60000, "피노누아", 3.5f);
Wine wine7 = createWine("와인7", "레드", "미국", 15000, "카베르네 소비뇽", 4.2f);
Wine wine8 = createWine("와인8", "화이트", "독일", 25000, "리슬링", 4.3f);
Wine wine9 = createWine("와인9", "스파클링", "프랑스", 35000, "샴페인", 4.6f);
Wine wine10 = createWine("와인10", "디저트", "포르투갈", 45000, "포트 와인", 4.7f);
Wine wine11 = createWine("와인11", "레드", "스페인", 55000, "템프라니요", 4.1f);
Wine wine12 = createWine("와인12", "화이트", "뉴질랜드", 65000, "소비뇽 블랑", 4.4f);
wineRepository.saveAll(List.of(wine1, wine2, wine3, wine4, wine5, wine6, wine7, wine8, wine9, wine10, wine11, wine12));
// when
List<Wine> recommendWines = wineRepository.findRecommendWinesBy(List.of("프랑스", "이탈리아"), List.of("레드", "화이트"), 60000L);
// then
assertThat(recommendWines)
.hasSize(2)
.extracting("name")
.containsExactlyInAnyOrder("와인1", "와인4");
}