Skip to content

test: CourseService 단위 테스트 추가 - #213

Merged
unam98 merged 2 commits into
devfrom
tests/course-service-unit-tests
Aug 5, 2026
Merged

test: CourseService 단위 테스트 추가#213
unam98 merged 2 commits into
devfrom
tests/course-service-unit-tests

Conversation

@unam98

@unam98 unam98 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

배경

테스트 커버리지 확장 2단계. 앱 핵심 도메인인 CourseService(코스 생성/조회/수정/삭제) 전체 메서드에 대해 단위 테스트 작성. 테스트 작성 중 발견한 실제 버그 3건도 함께 수정.

테스트

CourseServiceTest (24개) + RunnectUserTest (7개, 신규 equals/hashCode 검증)

  • createCourse: 정상 생성, 유저 없음, 좌표 부족, 출발지 주소 불완전
  • getCourseByUser / getPrivateCourseByUser: 정상 매핑, 빈 목록, 유저 없음
  • getCourseDetail: 본인/타인 코스, 업로더 없는 코스, 유저/코스 없음
  • updateCourse: 정상 수정, 코스 없음, 소유자 아니면 거부
  • deleteCourses: 비공개/공개 코스 삭제, 다건 삭제, 빈 목록, 존재하지 않는 코스, 중간 실패 시 이후 미처리 확인

발견해서 수정한 버그 3건

  1. DepartureConverter: 출발지 주소가 공백 기준 3토큰 미만이면 null을 반환해 CourseService에서 그대로 NPE로 이어졌음 → BadRequestException(VALIDATION_DEPARTURE_ADDRESS_EXCEPTION)을 던지도록 변경
  2. CourseService.updateCourse (IDOR): courseId로만 조회하고 userId로 소유권 검증을 하지 않아 다른 사람 코스의 제목도 수정 가능했음 → findByCourseIdAndUserId로 변경 (deleteCourses와 동일 패턴)
  3. RunnectUser equals/hashCode 부재: 참조 동일성에 의존해, 같은 유저라도 조회 경로가 다르면(로그인 유저 vs 코스에 매핑된 유저 등) 다른 사람으로 오판정될 수 있었음 → id 기준 equals/hashCode 추가 (Course.isMatchedUser, RecordService, PublicCourseService의 동일 패턴 전부 개선됨)

검증

  • 로컬에서 신규 테스트 31개(CourseServiceTest 24 + RunnectUserTest 7) 전부 통과
  • 로컬 DB/Redis 띄우고 ./gradlew build 전체(기존 ServerApplicationTests 포함) 통과 확인

createCourse/getCourseByUser/getPrivateCourseByUser/getCourseDetail/updateCourse/
deleteCourses 전체 메서드에 대해 정상 케이스 + 예외 케이스 + 경계값을 검증.

테스트 작성 중 실제 프로덕션 코드에서 3가지 의심되는 부분을 발견해 별도로 표시해둠:
- createCourse: 출발지 주소가 3토큰 미만이면 DepartureConverter가 null을 반환하고
  이후 NPE로 이어짐 (요청값 검증 부재)
- updateCourse: courseId로만 조회하고 userId로 소유자 검증을 하지 않아,
  다른 사람의 코스 제목도 수정 가능 (IDOR 의심)
- getCourseDetail: RunnectUser가 equals/hashCode를 오버라이드하지 않아 isNowUser 판정이
  참조 동일성에 의존함 (같은 id라도 인스턴스가 다르면 다른 사람으로 판정될 수 있음)
@unam98 unam98 self-assigned this Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Added comprehensive Mockito and JUnit coverage for CourseService. Tests cover course creation, retrieval, detail lookup, updates, deletion, validation errors, missing resources, ownership, uploader identity, and batch deletion behavior.

Changes

CourseService test coverage

Layer / File(s) Summary
CourseService behavior tests
src/test/java/org/runnect/server/course/service/CourseServiceTest.java
Adds shared fixtures and nested tests for course operations, response mappings, entity mutations, repository interactions, exceptions, invalid inputs, ownership, public-course cleanup, empty batches, and deletion failures.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 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
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the addition of CourseService unit tests, which matches the pull request changes.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tests/course-service-unit-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.

@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: 1

🧹 Nitpick comments (3)
src/test/java/org/runnect/server/course/service/CourseServiceTest.java (3)

93-111: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Set isPrivate explicitly in buildCourse.

The helper only writes isPrivate when the argument is false. The private case depends on the Course entity default. If that default changes, the tests keep passing while the fixture state is wrong.

♻️ Proposed change
         ReflectionTestUtils.setField(course, "id", id);
-        if (!isPrivate) {
-            ReflectionTestUtils.setField(course, "isPrivate", false);
-        }
+        ReflectionTestUtils.setField(course, "isPrivate", isPrivate);
         return course;
🤖 Prompt for 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.

In `@src/test/java/org/runnect/server/course/service/CourseServiceTest.java`
around lines 93 - 111, Update the buildCourse helper to explicitly set the
Course isPrivate field from the isPrivate argument for both private and public
fixtures, removing reliance on the entity default while preserving the existing
course construction.

175-184: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Two tests assert suspected production defects as expected behavior. Both tests pass because the production code is defective. After a fix, both fail and look like regressions instead of confirmations. At each site, either mark the test @Disabled with a tracking reference, or assert the intended behavior so the failure marks the open defect.

  • src/test/java/org/runnect/server/course/service/CourseServiceTest.java#L175-L184: replace the NullPointerException assertion with BadRequestException, or disable the test and link the departureAddress validation defect.
  • src/test/java/org/runnect/server/course/service/CourseServiceTest.java#L294-L307: replace isFalse() with isTrue() for the same-id uploader, or disable the test and link the id-comparison defect in CourseService.getCourseDetail.
🤖 Prompt for 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.

In `@src/test/java/org/runnect/server/course/service/CourseServiceTest.java`
around lines 175 - 184, The tests currently encode suspected production defects
as expected behavior. In
src/test/java/org/runnect/server/course/service/CourseServiceTest.java:175-184,
update 출발지_주소가_불완전하면_NPE() to expect BadRequestException for invalid
departureAddress, or disable it with a tracking reference; in the same
file:294-307, change the same-id uploader assertion from isFalse() to isTrue()
for CourseService.getCourseDetail, or disable it with a tracking reference for
the id-comparison defect.

453-466: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Clarify the partial-delete assertion scope.

CourseService.deleteCourses is @Transactional, so the mocked in-memory course1.setDeletedAt() state may not persist with a real database. Add a comment that this assertion covers only failed mocked-entity state, and cover the rollback behavior with an integration test.

🤖 Prompt for 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.

In `@src/test/java/org/runnect/server/course/service/CourseServiceTest.java`
around lines 453 - 466, The test method 중간에_실패하면_이후_항목은_처리되지_않는다의
course1.getDeletedAt() 검증이 mocked in-memory 엔티티 상태만 확인한다는 주석을 추가하고, 실제 트랜잭션 롤백
여부는 CourseService.deleteCourses를 사용하는 통합 테스트에서 검증하도록 보완한다.
🤖 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/org/runnect/server/course/service/CourseServiceTest.java`:
- Around line 294-307: Update 같은_id여도_인스턴스가_다르면_다른_사람으로_판정된다() to assert
isNowUser is true for distinct RunnectUser instances sharing the same id, so the
test captures the intended identity-by-id rule and fails until CourseService is
corrected.

---

Nitpick comments:
In `@src/test/java/org/runnect/server/course/service/CourseServiceTest.java`:
- Around line 93-111: Update the buildCourse helper to explicitly set the Course
isPrivate field from the isPrivate argument for both private and public
fixtures, removing reliance on the entity default while preserving the existing
course construction.
- Around line 175-184: The tests currently encode suspected production defects
as expected behavior. In
src/test/java/org/runnect/server/course/service/CourseServiceTest.java:175-184,
update 출발지_주소가_불완전하면_NPE() to expect BadRequestException for invalid
departureAddress, or disable it with a tracking reference; in the same
file:294-307, change the same-id uploader assertion from isFalse() to isTrue()
for CourseService.getCourseDetail, or disable it with a tracking reference for
the id-comparison defect.
- Around line 453-466: The test method 중간에_실패하면_이후_항목은_처리되지_않는다의
course1.getDeletedAt() 검증이 mocked in-memory 엔티티 상태만 확인한다는 주석을 추가하고, 실제 트랜잭션 롤백
여부는 CourseService.deleteCourses를 사용하는 통합 테스트에서 검증하도록 보완한다.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0336c746-7efc-46cc-83b4-631b3fa9e164

📥 Commits

Reviewing files that changed from the base of the PR and between 1ea4b11 and 750d191.

📒 Files selected for processing (1)
  • src/test/java/org/runnect/server/course/service/CourseServiceTest.java

1. DepartureConverter: 출발지 주소가 3토큰 미만이면 null 대신
   BadRequestException(VALIDATION_DEPARTURE_ADDRESS_EXCEPTION)을 던지도록 변경
   (기존엔 CourseService에서 바로 NPE로 이어짐)
2. CourseService.updateCourse: findById → findByCourseIdAndUserId로 변경해
   본인 소유 코스만 수정 가능하도록 수정 (IDOR 방지, deleteCourses와 동일 패턴)
3. RunnectUser: equals/hashCode를 id 기준으로 구현. 기존엔 참조 동일성에 의존해
   같은 유저라도 인스턴스가 다르면(Course.isMatchedUser, RecordService,
   PublicCourseService 등에서) 다른 사람으로 오판정될 수 있었음

CourseServiceTest의 관련 3개 테스트를 수정된 동작에 맞게 갱신하고,
RunnectUserTest를 새로 추가해 equals/hashCode 자체를 검증.
@unam98
unam98 merged commit 5754c13 into dev Aug 5, 2026
2 checks passed
@unam98
unam98 deleted the tests/course-service-unit-tests branch August 5, 2026 09:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants