🧪 MockDiaryStore란?
실제 DB(Core Data)나 파일 I/O 없이도 ViewModel 로직만 빠르게 테스트하려고 만든 가짜(Store 대역) 입니다.
- 실제 앱: DiaryStore ← Core Data와 통신
- 테스트: MockDiaryStore ← 메모리 값만 가지고 흉내
즉, DiaryProviding 프로토콜을 구현하되, 내부 동작은 간단한 배열/고정값으로 대체합니다.
🧱 만들기 레시피 (외워두기)
- “대상 인터페이스(프로토콜)”를 고른다 → DiaryProviding
- 그 프로토콜을 채택하는 테스트 전용 클래스를 만든다 → MockDiaryStore
- 테스트가 원하는 가짜 데이터 저장소(배열/딕셔너리 등)를 만든다 → mockDiaries, mockImages
- 프로토콜 요구 메서드·프로퍼티를 그 가짜 데이터로 채워서 리턴한다
최소 예시
import Combine
import UIKit
@testable import LemonLog
@MainActor
final class MockDiaryStore: DiaryProviding {
// 1) 테스트에서 주입/세팅할 가짜 데이터
var mockDiaries: [EmotionDiaryModel] = []
var mockImages: [(UIImage?, String)] = []
// 2) 프로토콜 요구사항 구현
var diariesPublisher: AnyPublisher<[EmotionDiaryModel], Never> {
Just(mockDiaries).eraseToAnyPublisher() // 👉 아래 “왜 Just?” 참고
}
var snapshot: [EmotionDiaryModel] { mockDiaries }
func diary(with id: String) -> EmotionDiaryModel? {
mockDiaries.first { $0.id.uuidString == id }
}
func diaries(inWeekOf date: Date) -> [EmotionDiaryModel] {
mockDiaries
}
func countByEmotion(inWeekOf date: Date) -> [EmotionCategory: Int] {
Dictionary(grouping: mockDiaries) { EmotionCategory(rawValue: $0.emotion) ?? .happy_grade_1 }
.mapValues(\.count)
}
func fetchFirstImages() async -> [(UIImage?, String)] {
mockImages
}
}
❓자주 묻는 것들
1) LemonLog.EmotionDiaryModel로 써도 돼?
네. 같아요.
@testable import LemonLog 되어 있으면
보통 EmotionDiaryModel로 바로 씁니다.
네이밍 충돌이 있거나 명시적으로 표시하고 싶을 때만
LemonLog.EmotionDiaryModel처럼 모듈 접두사를 붙이세요.
2) Just는 왜 쓰는 거야?
Just는 “값 하나를 즉시 한 번만 방출(emit)하고 완료하는” 간단한 Publisher예요.
모의 환경에서 “초기값 한 번만 흘려보내면 되는” 경우에 딱 좋아요.
diariesPublisher가 “현재 리스트는 이거야” 라고
한 번만 알려주면 충분한 테스트(예: 최근 5개 제한, 통계 계산)에서 가볍고 빠릅니다.
그럼 실시간 갱신 테스트는?
저장/수정/삭제처럼 여러 번 갱신되는 흐름까지 테스트하고 싶다면 Just 대신:
- CurrentValueSubject<[EmotionDiaryModel], Never> (현재값 보관 + 여러 번 보냄)
- PassthroughSubject<[EmotionDiaryModel], Never> (현재값 보관 없음, 보낼 때만 방출)
중 하나로 바꾸고, 테스트 중간중간 subject.send(newValue)로 여러 번 값을 내보내세요.
final class MockDiaryStoreStreaming: DiaryProviding {
private let subject = CurrentValueSubject<[EmotionDiaryModel], Never>([])
var diariesPublisher: AnyPublisher<[EmotionDiaryModel], Never> { subject.eraseToAnyPublisher() }
var snapshot: [EmotionDiaryModel] { subject.value }
// 테스트에서 호출할 도우미
func push(_ diaries: [EmotionDiaryModel]) { subject.send(diaries) }
// 나머지 프로토콜도 동일하게 채움…
}
3) diariesPublisher는 무슨 역할?
“일기 배열이 바뀔 때마다 구독자에게 알려주는 스트림” 입니다.
- 실제 앱에선 DiaryStore가 Core Data 변화를 감지해 send()로 내보냅니다.
- ViewModel(HomeViewModel)은 이 Publisher를 구독해서 UI 상태(@Published) 를 갱신하죠.
- Mock에서는 Just로 한 번만 내보내거나, Subject로 여러 번 내보내며 상황을 시뮬레이션합니다.
✅ 요약
- MockDiaryStore = 진짜 Store의 “가짜” (DB 없이 빠르게 ViewModel만 검증)
- Just = 값 1회 방출(간단한 초기 검증에 최적)
- 실시간 갱신 검증은 Subject로 바꾸고 send() 호출
- diariesPublisher = ViewModel이 실시간으로 상태를 받는 통로
- 타입 표기는 EmotionDiaryModel / LemonLog.EmotionDiaryModel 둘 다 OK (상황에 맞게)
'감정일기(가칭)' 카테고리의 다른 글
| “테스트 실행 타이밍”과 “ViewModel 초기화 타이밍”이 안 맞는 상황 해결 (0) | 2025.10.24 |
|---|---|
| 초보자도 쉽게 이해할 수 있는 단위 테스트의 기본 구조 - HomeViewModelTests (0) | 2025.10.24 |
| DiaryStore 테스트 했는데.. 이를 가져다 사용하는 HomeViewModel을 테스트해야하나? (0) | 2025.10.24 |
| 🧩 Swift 6에서 @MainActor와 Singleton(.shared) 접근 시 발생하는 오류 해결하기 (0) | 2025.10.24 |
| 🍋 DiaryStore 이후의 ViewModel 설계 — 화면 단위로 나누는 이유와 구조 (0) | 2025.10.24 |