Skip to content

✨ [OMF-340] 단위 테스트 추가 - 잔여 서비스 레이어 전체 (PR 3/3)#158

Merged
KJaeKwan merged 4 commits into
developfrom
feat/OMF-340-discount-query-tests
May 12, 2026
Merged

✨ [OMF-340] 단위 테스트 추가 - 잔여 서비스 레이어 전체 (PR 3/3)#158
KJaeKwan merged 4 commits into
developfrom
feat/OMF-340-discount-query-tests

Conversation

@KJaeKwan

@KJaeKwan KJaeKwan commented May 10, 2026

Copy link
Copy Markdown
Collaborator

✨ Related Issue

OMF-340 단위 테스트 작성 - 잔여 서비스 레이어 전체 (PR 3/3)


📌 Task Details

원래 PR 3~9로 나눌 예정이었던 잔여 범위를 하나의 PR로 통합합니다.

새로 추가된 테스트 파일 (11개, 총 64개 테스트)

  • DiscountCodeQueryServiceTest (8개)

    • validate: 코드 없음 → NOT_FOUND, 만료 → EXPIRED, 유효 → eligible=true
    • getByCode: 코드 없음 → 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), memberNotFound
  • SurveyGlobalStatsServiceTest (7개)

    • addDueCount/addCompletedCount/addPromotionCount: 기존 stats 증가, stats 없을 때 init 후 증가
    • getStats: 존재 시 dailyUserCount 포함 반환, 없을 때 init 기본값
    • removeOldDailyUsers: Redis rangeRemove 호출 확인
  • ResponseQueryServiceTest (4개)

    • 필터 없음 → 직접 위임, filter=null → 필터 없는 메서드, filter 비어있음 → 필터 없는 메서드, 유효 filter → 필터 포함 메서드
  • 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개로 재구성)

PR 범위
PR 1/3 순수 비즈니스 로직 (환불/변환/CSV/할인코드)
PR 2/3 Member 도메인
PR 3/3 (현재) 잔여 서비스 레이어 전체

테스트 스택: JUnit 5 + Mockito (@ExtendWith(MockitoExtension.class)) + AssertJ


💬 Review Requirements (Optional)

Summary by CodeRabbit

릴리스 노트

  • 테스트
    • 할인 코드, 설문 및 질문 관리, 응답 추적, 양식 요청 및 제출 워크플로우 등 시스템의 주요 기능에 대한 포괄적인 단위 테스트 커버리지가 추가되었습니다.

Review Change Stack

DiscountCodeQueryService / SurveyQueryService / SurveyCommandService
SurveyGlobalStatsService / ResponseQueryService — 총 36개 테스트
@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d2227be1-8aca-47ab-9c20-4350ca431ee5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

워크스루

이 PR은 10개의 새로운 JUnit 5 테스트 클래스를 추가하여 할인, 참여, 질문, 설문 도메인의 핵심 서비스들을 포괄적으로 검증합니다. 모든 테스트는 Mockito를 사용한 종속성 모킹과 ReflectionTestUtils를 통한 엔티티 설정을 활용하며, 정상 경로, 예외 경로, 경계 조건을 다룹니다.

변경 사항

할인 및 응답 도메인 테스트

계층 / 파일 요약
할인 코드 쿼리 서비스
src/test/java/OneQ/OnSurvey/domain/discount/service/DiscountCodeQueryServiceTest.java
validate, getByCode, findAll 메서드를 테스트합니다. 미발견 및 만료 코드에 대한 예외 케이스와 활성 코드가 먼저, 만료 날짜 오름차순으로 정렬되는 결과를 검증합니다.
참여 답변 커맨드 서비스
src/test/java/OneQ/OnSurvey/domain/participation/service/answer/QuestionAnswerCommandServiceTest.java, ScreeningAnswerCommandServiceTest.java
updateResponseAfterQuestionAnswersupdateResponseAfterScreening 동작을 테스트하며, 응답 생성, 기존 응답 업데이트, 및 답변 삽입을 검증합니다. 선택지 매칭 여부에 따른 screening 플래그 설정을 확인합니다.
참여 응답 쿼리 서비스
src/test/java/OneQ/OnSurvey/domain/participation/service/response/ResponseQueryServiceTest.java
getResponseCountBySurveyId가 필터 조건(null, 빈 값, 채워진 값)에 따라 올바른 저장소 메서드에 위임하는지 확인합니다.

질문 도메인 테스트

계층 / 파일 요약
질문 커맨드 서비스
src/test/java/OneQ/OnSurvey/domain/question/service/QuestionCommandServiceTest.java
upsertSections의 유효성 검사(null/blank 제목, null 순서)와 섹션 생성, 업데이트, 삭제 흐름을 테스트합니다.
질문 쿼리 서비스
src/test/java/OneQ/OnSurvey/domain/question/service/QuestionQueryServiceTest.java
ChoiceOptionGridOption 엔티티를 DTO로 변환하는 로직과 질문 개수 위임을 검증합니다.

설문 도메인 테스트

계층 / 파일 요약
설문 글로벌 통계 서비스
src/test/java/OneQ/OnSurvey/domain/survey/service/SurveyGlobalStatsServiceTest.java
addDueCount, addCompletedCount, addPromotionCount의 증분 로직을 기존 통계 존재 여부에 따라 테스트하며, getStats()가 저장소와 Redis 데이터를 결합하여 반환하는지 확인합니다.
설문 커맨드 서비스
src/test/java/OneQ/OnSurvey/domain/survey/service/command/SurveyCommandServiceTest.java
upsertScreening의 null/blank 삭제 및 생성/업데이트 동작과 refundSurvey의 다중 예외 경로(미발견, non-refundable, 0 환불액) 및 성공 경로(코인 증가, 상태 전환)를 테스트합니다.
설문 양식 요청 서비스
src/test/java/OneQ/OnSurvey/domain/survey/service/formRequest/FormCommandServiceTest.java, FormQueryServiceTest.java
markAsRegisteredpublishFormRequest의 예외 경로(미발견, 미등록, 멤버 미발견/다중)와 성공 경로를 검증하며, 이메일 할당량 계산 및 미등록 요청 목록 조회를 테스트합니다.
설문 쿼리 서비스
src/test/java/OneQ/OnSurvey/domain/survey/service/query/SurveyQueryServiceTest.java
ID별 설문 조회, 프로모션 액수 반환, 사용자 설문 목록 조회 및 createdAt 역순 정렬, 상태별 분리(ongoing/refunded)를 테스트합니다.

예상 코드 리뷰 노력

🎯 2 (단순) | ⏱️ ~12분

관련된 PR

  • On-Survey/Backend#139: 할인 코드 정렬 동작 테스트와 구현이 직접 대응됩니다.
  • On-Survey/Backend#126: QuestionQueryService 및 SurveyQueryService 테스트가 추가된 API를 직접 검증합니다.
  • On-Survey/Backend#130: DiscountCodeQueryService 테스트가 해당 PR에서 도입된 클래스를 검증합니다.

제안된 레이블

♻️refactor

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 PR의 주요 목적인 '단위 테스트 추가 - 잔여 서비스 레이어 전체'를 명확하게 설명하고 있으며, 작업 추적 ID(OMF-340)와 PR 순서(3/3)를 포함하여 구체적입니다.
Description check ✅ Passed PR 설명은 관련 이슈 링크, 세부 작업 내용(11개 테스트 파일, 64개 테스트), 전체 PR 계획 테이블을 포함하여 템플릿의 필수 섹션을 충족하고 있으나, Review Requirements 섹션이 비어있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/OMF-340-discount-query-tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@KJaeKwan KJaeKwan changed the title ✨ [OMF-340] 잔여 서비스 레이어 단위 테스트 일괄 작성 ✨ [OMF-340] 단위 테스트 추가 - 잔여 서비스 레이어 전체 (PR 3/3) May 10, 2026
@KJaeKwan KJaeKwan self-assigned this May 10, 2026
@KJaeKwan KJaeKwan added the ✅test 테스트 관련 label May 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between fc84efc and 5acd05f.

📒 Files selected for processing (12)
  • gradlew
  • src/test/java/OneQ/OnSurvey/domain/discount/service/DiscountCodeQueryServiceTest.java
  • src/test/java/OneQ/OnSurvey/domain/participation/service/answer/QuestionAnswerCommandServiceTest.java
  • src/test/java/OneQ/OnSurvey/domain/participation/service/answer/ScreeningAnswerCommandServiceTest.java
  • src/test/java/OneQ/OnSurvey/domain/participation/service/response/ResponseQueryServiceTest.java
  • src/test/java/OneQ/OnSurvey/domain/question/service/QuestionCommandServiceTest.java
  • src/test/java/OneQ/OnSurvey/domain/question/service/QuestionQueryServiceTest.java
  • src/test/java/OneQ/OnSurvey/domain/survey/service/SurveyGlobalStatsServiceTest.java
  • src/test/java/OneQ/OnSurvey/domain/survey/service/command/SurveyCommandServiceTest.java
  • src/test/java/OneQ/OnSurvey/domain/survey/service/formRequest/FormCommandServiceTest.java
  • src/test/java/OneQ/OnSurvey/domain/survey/service/formRequest/FormQueryServiceTest.java
  • src/test/java/OneQ/OnSurvey/domain/survey/service/query/SurveyQueryServiceTest.java

@KJaeKwan KJaeKwan merged commit 210a140 into develop May 12, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✅test 테스트 관련

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant