✨ [OMF-340] 단위 테스트 추가 - 잔여 서비스 레이어 전체 (PR 3/3)#158
Conversation
DiscountCodeQueryService / SurveyQueryService / SurveyCommandService SurveyGlobalStatsService / ResponseQueryService — 총 36개 테스트
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 Walkthrough워크스루이 PR은 10개의 새로운 JUnit 5 테스트 클래스를 추가하여 할인, 참여, 질문, 설문 도메인의 핵심 서비스들을 포괄적으로 검증합니다. 모든 테스트는 Mockito를 사용한 종속성 모킹과 ReflectionTestUtils를 통한 엔티티 설정을 활용하며, 정상 경로, 예외 경로, 경계 조건을 다룹니다. 변경 사항할인 및 응답 도메인 테스트
질문 도메인 테스트
설문 도메인 테스트
예상 코드 리뷰 노력🎯 2 (단순) | ⏱️ ~12분 관련된 PR
제안된 레이블
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/test/java/OneQ/OnSurvey/domain/discount/service/DiscountCodeQueryServiceTest.java`:
- Around line 106-123: Add an edge-case unit test to verify how codes with
expiration equal to LocalDate.now() are classified by
DiscountCodeQueryService.findAll(): create a new test method in
DiscountCodeQueryServiceTest that builds a code with expiry today (using
buildCode("TODAY", LocalDate.now())) and at least one clearly active code, mock
discountCodeRepository.findAll() to return them, call
discountCodeQueryService.findAll(), and assert the active code appears before
the today-expiring code (and that sizes/order are as expected) to ensure "today"
is treated as expired by the sorting logic.
- Around line 37-69: Add Mockito verify checks to each test to assert the
repository was called with the expected code: after the existing assertions in
validate_notFound_throwsException, validate_expired_throwsException and
validate_valid_returnsEligibleTrue add
verify(discountCodeRepository).findByCode("XXXXXX"/"AAAAAA"/"BBBBBB") (or
verify(..., times(1)) if you prefer) to ensure findByCode was invoked with the
correct parameter; optionally add
verifyNoMoreInteractions(discountCodeRepository) to assert no other repository
calls occurred.
In
`@src/test/java/OneQ/OnSurvey/domain/participation/service/answer/QuestionAnswerCommandServiceTest.java`:
- Line 69: 현재 테스트 uses verify(responseRepository, never()).save(response) which
only asserts save not called with that exact response instance; change the
assertion to ensure save was never called at all by using
verify(responseRepository, never()).save(any(Response.class)) (or equivalent
Mockito any matcher) so the test fails if the service creates and saves a new
Response; update imports/matchers if needed and retain other assertions in
QuestionAnswerCommandServiceTest and references to responseRepository, save,
never(), and response.
In
`@src/test/java/OneQ/OnSurvey/domain/participation/service/response/ResponseQueryServiceTest.java`:
- Around line 49-52: Update the test to assert that the overloaded method with
any filter is never called: instead of verifying only
getResponseCountBySurveyId(1L, null) is not called, change the verification for
responseRepository.getResponseCountBySurveyId to use Mockito matchers (e.g.,
eq(1L) for the id and any()/any(Filter.class) for the filter) with never(), so
the test ensures no call to the overload getResponseCountBySurveyId(1L, <any
filter>) occurs; locate the call site in ResponseQueryServiceTest where result
is asserted and the two verify(...) lines appear and replace the second verify
accordingly.
In
`@src/test/java/OneQ/OnSurvey/domain/survey/service/command/SurveyCommandServiceTest.java`:
- Around line 193-225: The refundSurvey flow lacks ownership authorization
checks allowing non-owners to trigger refunds; update the
SurveyCommandService.refundSurvey method to call
AuthorizationUtils.validateOwnershipOrAdmin(userKey, survey.getMemberId()) (or
equivalent) after loading the Survey and before performing refund logic, and
throw the SURVEY_FORBIDDEN error (MemberErrorCode.SURVEY_FORBIDDEN or the
existing error enum) when validation fails; then add a unit test in
SurveyCommandServiceTest that calls surveyCommandService.refundSurvey with a
different userKey than survey.getMemberId() and asserts a CustomException with
SURVEY_FORBIDDEN is thrown.
In
`@src/test/java/OneQ/OnSurvey/domain/survey/service/formRequest/FormQueryServiceTest.java`:
- Around line 41-55: The tests for FormQueryService.getEmailQuota use a loose
anyString() stub on redisCacheAction.getIntValue which masks incorrect key
composition; update at least one test to stub and/or verify the exact Redis key
used (e.g., replace
given(redisCacheAction.getIntValue(anyString())).willReturn(0) with
given(redisCacheAction.getIntValue(eq("email_quota:1"))).willReturn(0) or add a
Mockito.verify(redisCacheAction).getIntValue("email_quota:1") after calling
formQueryService.getEmailQuota(1L)), referencing redisCacheAction.getIntValue
and formQueryService.getEmailQuota to ensure the key prefix + user id is
correct.
In
`@src/test/java/OneQ/OnSurvey/domain/survey/service/SurveyGlobalStatsServiceTest.java`:
- Around line 52-59: The test addDueCount_increasesExistingStats (and the
similar tests around it) verifies entity state but not whether the repository
save path is invoked; update the test to explicitly verify repository
interaction by adding a Mockito verify call for statsRepository.save(stats)
after invoking surveyGlobalStatsService.addDueCount(100L) (or, if the service is
intended to rely on JPA dirty checking, add a comment asserting that save is
intentionally not called and leave as-is). Ensure you reference the
statsRepository mock and the stats instance used in the test when adding the
verification.
- Around line 97-108: Add stricter argument verification for mocked redis calls
in tests: in getStats_existingStats_returnsCorrectValues verify that
redisAgent.getZSetCount was called with the expected key (e.g., "daily:user:")
using verify(redisAgent).getZSetCount(eq("daily:user:"), anyLong(), anyLong()),
and in removeOldDailyUsers test capture and assert the actual arguments passed
to redisAgent.rangeRemoveFromZSet by using ArgumentCaptor for String and Long to
verify the key equals "daily:user:" (and optionally assert start/end values);
update the tests referencing methods
getStats_existingStats_returnsCorrectValues, redisAgent.getZSetCount,
removeOldDailyUsers, and redisAgent.rangeRemoveFromZSet accordingly.
- Around line 73-93: Add two tests mirroring the existing
addDueCount_noStats_initAndIncreases pattern to cover the "no stats" case for
addCompletedCount and addPromotionCount: create tests named
addCompletedCount_noStats_initAndIncreases and
addPromotionCount_noStats_initAndIncreases that stub
statsRepository.findById(1L) to return Optional.empty(), call
surveyGlobalStatsService.addCompletedCount(delta) and
surveyGlobalStatsService.addPromotionCount(delta) respectively, then verify a
new SurveyGlobalStats was created/initialized and saved with the expected totals
(delta applied to initial zeros) by asserting saved object's
getTotalCompletedCount()/getTotalPromotionCount() or by verifying
statsRepository.save(...) was called with an object whose counters equal the
delta; reference methods SurveyGlobalStatsService.addCompletedCount,
SurveyGlobalStatsService.addPromotionCount and statsRepository.findById/save to
locate the code to modify.
- Around line 63-71: The test currently stubs statsRepository.save to return a
pre-created newStats and then asserts on that object; instead capture the actual
object passed to statsRepository.save using an ArgumentCaptor and assert its
state after calling surveyGlobalStatsService.addDueCount(50L). Replace the
given(statsRepository.save(any(SurveyGlobalStats.class))).willReturn(newStats)
with an ArgumentCaptor<SurveyGlobalStats> captor, call
surveyGlobalStatsService.addDueCount(50L),
verify(statsRepository).save(captor.capture()), then
assertThat(captor.getValue().getTotalDueCount()).isEqualTo(1050L) (using
SurveyGlobalStats.init() only to stub findById as before).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 8ec2a114-0f66-4562-acb4-8687c4ca2b44
📒 Files selected for processing (12)
gradlewsrc/test/java/OneQ/OnSurvey/domain/discount/service/DiscountCodeQueryServiceTest.javasrc/test/java/OneQ/OnSurvey/domain/participation/service/answer/QuestionAnswerCommandServiceTest.javasrc/test/java/OneQ/OnSurvey/domain/participation/service/answer/ScreeningAnswerCommandServiceTest.javasrc/test/java/OneQ/OnSurvey/domain/participation/service/response/ResponseQueryServiceTest.javasrc/test/java/OneQ/OnSurvey/domain/question/service/QuestionCommandServiceTest.javasrc/test/java/OneQ/OnSurvey/domain/question/service/QuestionQueryServiceTest.javasrc/test/java/OneQ/OnSurvey/domain/survey/service/SurveyGlobalStatsServiceTest.javasrc/test/java/OneQ/OnSurvey/domain/survey/service/command/SurveyCommandServiceTest.javasrc/test/java/OneQ/OnSurvey/domain/survey/service/formRequest/FormCommandServiceTest.javasrc/test/java/OneQ/OnSurvey/domain/survey/service/formRequest/FormQueryServiceTest.javasrc/test/java/OneQ/OnSurvey/domain/survey/service/query/SurveyQueryServiceTest.java
✨ Related Issue
OMF-340 단위 테스트 작성 - 잔여 서비스 레이어 전체 (PR 3/3)
📌 Task Details
원래 PR 3~9로 나눌 예정이었던 잔여 범위를 하나의 PR로 통합합니다.
새로 추가된 테스트 파일 (11개, 총 64개 테스트)
DiscountCodeQueryServiceTest(8개)validate: 코드 없음 → NOT_FOUND, 만료 → EXPIRED, 유효 → eligible=truegetByCode: 코드 없음 → NOT_FOUND, 만료 → EXPIRED, 유효 → 엔티티 반환findAll: 활성 코드 먼저 + 만료일 오름차순 정렬, 빈 목록SurveyQueryServiceTest(7개)getSurveyById: 존재/없음getPromotionAmountBySurveyId: surveyInfo 존재/없음getMySurveys: ONGOING/CLOSED → ongoing, REFUNDED → refunded 분리, createdAt 내림차순, 빈 목록SurveyCommandServiceTest(10개)upsertScreening: null/blank → 삭제, 신규 생성, 기존 업데이트refundSurvey: surveyNotFound, infoNotFound, notRefundable, refundAmount≤0, 성공(코인 지급+REFUNDED), memberNotFoundSurveyGlobalStatsServiceTest(7개)addDueCount/addCompletedCount/addPromotionCount: 기존 stats 증가, stats 없을 때 init 후 증가getStats: 존재 시 dailyUserCount 포함 반환, 없을 때 init 기본값removeOldDailyUsers: Redis rangeRemove 호출 확인ResponseQueryServiceTest(4개)QuestionQueryServiceTest(4개)getOptionsByQuestionIdList: 보기 → OptionDto 변환, 빈 목록getGridOptionsByQuestionIdList: 그리드 옵션 → GridOptionDto 변환countQuestionsBySurveyId: repository 위임QuestionCommandServiceTest(5개)upsertSections: null 제목 → 예외, 공백 제목 → 예외, null order → 예외, 신규 생성, 기존 업데이트, DB 초과 섹션 삭제ScreeningAnswerCommandServiceTest(4개)updateResponseAfterScreening: 기댓값 일치 → screened=false, 불일치 → screened=true, 신규 응답 생성insertAnswer: answerRepository.save + updateResponseAfterScreening 연쇄 호출QuestionAnswerCommandServiceTest(3개)updateResponseAfterQuestionAnswers: 응답 없음 → 신규 생성 저장, 미완료 → 저장, 완료 → 저장 안함FormQueryServiceTest(4개)getEmailQuota: 사용량 없음 → 전체 한도, 사용량 있음 → 차감 한도getAllUnregisteredRequests: 목록 반환, 빈 목록FormCommandServiceTest(6개)markAsRegistered: request 없음 → FORM_REQUEST_NOT_FOUND, 성공publishFormRequest: request 없음 → NOT_FOUND, 미등록 → NOT_YET_REGISTERED, 회원 없음 → MEMBER_NOT_FOUND, 복수 회원 → MEMBER_NOT_FOUND, 성공전체 PR 계획 (3개로 재구성)
테스트 스택: JUnit 5 + Mockito (
@ExtendWith(MockitoExtension.class)) + AssertJ💬 Review Requirements (Optional)
Summary by CodeRabbit
릴리스 노트