-
Notifications
You must be signed in to change notification settings - Fork 4
✅ test: feed-crawler 유닛 테스트 작성 #489
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
이 PR은 feed-crawler 모듈의 핵심 비즈니스 로직에 대한 포괄적인 유닛 테스트를 추가합니다. 추상 클래스 패턴을 사용하는 코드의 템플릿 메서드 실행 흐름과 에러 처리를 중점적으로 검증하며, RSS/Atom 파서, 크롤러 워크플로우, AI 워커의 재시도 로직 등을 테스트합니다.
주요 변경사항:
- 고정된 날짜를 사용하여 시간 의존적인 테스트의 안정성을 개선
- URL 기반 조건부 fetch 모킹으로 순서 의존성 제거
- 추상 클래스의 템플릿 메서드 패턴과 에러 핸들링 검증에 집중
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| feed-crawler/test/unit/parser.spec.ts | 고정 날짜 사용으로 시간 의존성 제거, URL 기반 조건부 모킹으로 테스트 안정성 향상 |
| feed-crawler/test/unit/parser-util.spec.ts | ParserUtil의 썸네일 추출, URL 처리, HTML 엔티티 변환 로직에 대한 포괄적 테스트 추가 |
| feed-crawler/test/unit/feed-parser-manager.spec.ts | 파서 선택 로직, 에러 복원력, short-circuit 동작 검증 테스트 추가 |
| feed-crawler/test/unit/feed-crawler.spec.ts | 크롤링 워크플로우, 조기 종료 시나리오, 병렬 처리 동작 테스트 추가 |
| feed-crawler/test/unit/claude-event-worker.spec.ts | AI 워커의 재시도 로직, deathCount 경계값, API 응답 파싱, rate limiting 테스트 추가 |
| feed-crawler/test/unit/abstract-queue-worker.spec.ts | 템플릿 메서드 패턴의 실행 순서와 에러 처리 검증 테스트 추가 |
| // 병렬 실행 검증: 첫 번째 호출에 지연을 주어 병렬 실행 시 순서가 뒤바뀌는지 확인 | ||
| mockFeedParserManager.fetchAndParse | ||
| .mockImplementationOnce(async () => { | ||
| await new Promise((resolve) => setTimeout(resolve, 50)); | ||
| callOrder.push(1); | ||
| return [mockFeedDetails[0]]; | ||
| }) | ||
| .mockImplementationOnce(async () => { | ||
| callOrder.push(2); | ||
| return [mockFeedDetails[1]]; | ||
| }); | ||
|
|
||
| // When | ||
| const result = await feedCrawler['feedGroupByRss']( | ||
| mockRssObjects, | ||
| startTime, | ||
| ); | ||
|
|
||
| // Then | ||
| expect(mockFeedParserManager.fetchAndParse).toHaveBeenCalledTimes(2); | ||
| expect(result).toEqual([[mockFeedDetails[0]], [mockFeedDetails[1]]]); | ||
| // 병렬 실행이면 지연이 없는 두 번째가 먼저 완료됨 | ||
| expect(callOrder).toEqual([2, 1]); | ||
| }); |
Copilot
AI
Nov 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] P5) [테스트 안정성] setTimeout을 사용한 병렬 처리 테스트는 테스트 환경이나 시스템 부하에 따라 불안정할 수 있습니다. 50ms 지연이 항상 순서를 보장하지 않을 수 있습니다.
더 안정적인 방법은:
Promise.all이 호출되는지 확인하거나- 지연 시간을 충분히 늘리거나 (100ms 이상)
- 각 호출이 완료되기 전에 다음 호출이 시작되는지 확인하는 방식
현재 테스트는 동작하지만, CI/CD 환경에서 간헐적으로 실패할 가능성이 있습니다.
| import { FeedParserManager } from '../../src/common/parser/feed-parser-manager'; | ||
| import { RssObj } from '../../src/common/types'; | ||
|
|
||
| // test시에는 필터링 하는 시간대가 매우 광범위하기에, 시간대를 신경 쓸 필요없음. |
Copilot
AI
Nov 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P4) [맞춤법] 주석의 맞춤법을 수정해야 합니다:
- "test시에는" → "테스트 시에는" (한글로 통일)
- "필터링 하는" → "필터링하는" (띄어쓰기)
- "필요없음" → "필요 없음" (띄어쓰기)
| // test시에는 필터링 하는 시간대가 매우 광범위하기에, 시간대를 신경 쓸 필요없음. | |
| // 테스트 시에는 필터링하는 시간대가 매우 광범위하기에, 시간대를 신경 쓸 필요 없음. |
|
|
||
| it('정상적인 feedDetail을 반환해야 한다.', async () => { | ||
| // 고정 날짜보다 이전 시간을 startTime으로 사용하여 피드가 필터링되도록 함 | ||
| const startTime = new Date(); |
Copilot
AI
Nov 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3) [문서화] 주석과 코드가 일치하지 않습니다. 주석에는 "고정 날짜보다 이전 시간을 startTime으로 사용하여 피드가 필터링되도록 함"이라고 설명하고 있지만, 코드는 new Date()로 현재 시간을 사용하고 있습니다.
FIXED_DATE는 테스트 상단에서 이미 new Date()로 생성되어 피드의 pubDate에 사용되고 있으므로, 여기서 다시 new Date()를 호출하면 약간의 시간차가 발생할 수 있습니다.
의도대로라면 다음과 같이 수정하는 것이 좋겠습니다:
// FIXED_DATE 이전 시간을 사용하여 모든 피드가 포함되도록 함
const startTime = new Date(FIXED_DATE.getTime() - 1000); // 1초 전또는 주석을 코드에 맞게 수정:
// 현재 시간을 startTime으로 사용 (테스트에서는 시간 범위가 광범위함)
const startTime = new Date();| const startTime = new Date(); | |
| const startTime = new Date(FIXED_DATE.getTime() - 1000); // 1초 전 |
|
|
||
| it('정상적인 feedDetail을 반환해야 한다.', async () => { | ||
| // 고정 날짜보다 이전 시간을 startTime으로 사용하여 피드가 필터링되도록 함 | ||
| const startTime = new Date(); |
Copilot
AI
Nov 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3) [문서화] 주석과 코드가 일치하지 않습니다. RSS 2.0 테스트(line 151-152)와 동일한 문제가 있습니다. 주석에는 "고정 날짜보다 이전 시간을 startTime으로 사용"이라고 하지만 실제로는 new Date()를 사용하고 있어 일관성이 없습니다.
| const startTime = new Date(); | |
| const startTime = FIXED_DATE; |
| ); | ||
|
|
||
| // When | ||
| const result = await claudeEventWorker['loadFeeds'](); |
Copilot
AI
Nov 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable result.
| const result = await claudeEventWorker['loadFeeds'](); | |
| await claudeEventWorker['loadFeeds'](); |
🔨 테스크
Issue
📋 작업 내용
추상클래스가 사용된 로직의 유닛 테스트를 어떻게 해야 할까?
추상 클래스를 활용하는 케이스는 현재 feed-crawler내에 크게 두가지가 존재합니다.
BaseFeedParser(이 경우는FeedParserManager를 테스틀하여 처리합니다.)AbstractQueueWorker이러한 패턴에서는 추상 클래스가 "공통적으로 어떤 순서로 무엇을 할지"를 정의하고, 구체 클래스는 그중 특정 구현체마다 달라진 작업을 "어떻게 할지"만 구현합니다. 이때 내부 구현 메서드들은
protected으로 캡슐화 되어 관리됩니다.이런식의 구성이 존재할 때, 저희는
processQueue()가 어떻게 동작하고 어떤 결과를 수행했는지는 관심이 없으며 테스트 대상에서 제외됩니다. (이 포스팅을 읽어보시면 좋습니다.)중점적으로 테스트 할 대상
근데
ClaudeEventWorker는 왜 테스트 하나요?ClaudeEventWorker는 AI 에이전트에게 별도의 호출이 필요한 케이스이기에 예외적으로 구현체를 테스트 하였습니다.그 외 핵심 로직 유닛 테스트 작성
feed-crawler 및 parser-util등 비즈니스 로직 실행에 필요한 로직들에 대한 테스트를 작성하였습니다.
요약
abstract-queue-worker.spec.tsparser.spec.tsfeed-parser-manager.spec.tsfeed-crawler.spec.tsclaude-event-worker.spec.ts