본문 바로가기
감정일기(가칭)

DiaryStore 테스트 했는데.. 이를 가져다 사용하는 HomeViewModel을 테스트해야하나?

by 밤새는 탐험가89 2025. 10. 24.
728x90
SMALL

🧩 1️⃣ DiaryStore vs HomeViewModel 테스트의 차이

구분 대상 테스트 목적 테스트 방식
DiaryStoreTests 데이터 레이어 (SSOT) Core Data와 Store의 CRUD 동작 검증 실제 DB 쓰기/읽기 테스트
HomeViewModelTests 뷰모델 레이어 (UI 로직) ViewModel이 Store 데이터를
올바르게 가공하고 반영하는지 검증
Store를 Mock으로 대체하여 테스트

 

즉,

- DiaryStoreTests는 데이터 저장이 진짜로 되는가? 를 검증

- HomeViewModelTests데이터가 화면에 맞게 잘 보여지는가? 를 검증

 

두 테스트는 서로 다른 책임을 테스트합니다.


🧠 2️⃣ 왜 HomeViewModel도 테스트해야 할까?

이유는 간단해요.

 

ViewModel은 “비즈니스 로직”을 담당합니다.


즉, 단순히 데이터를 보여주는 게 아니라

- 주간 감정 비율 계산

- 최근 5개의 일기만 노출

- 이미지 로드 후 Combine 반영
이런 “가공” 과정을 수행하죠.

 

그래서 테스트해야 하는 건 이런 부분들입니다 👇

테스트 항목 기대 결과
recentDiaries 항상 최신순으로 최대 5개까지만 들어있는가
emotionSummary countByEmotion이 올바르게 계산되는가
loadDiaryImages() 이미지 리스트가 정상적으로 채워지는가
Combine 구독 (observeStore) store의 변경이 즉시 반영되는가

🧩 3️⃣ 하지만! CoreData를 직접 쓰면 테스트가 느려져요 ⚠️

그래서 여기서 등장하는 게 Mock Store입니다 🎭

 

Mock은 “진짜 데이터베이스를 쓰지 않고, 가짜 데이터를 흉내 내는 객체”예요.

 

즉, HomeViewModel이 DiaryStore를 의존하지 않고,
DiaryProviding 프로토콜만 알고 있기 때문에
우리는 아래처럼 간단히 가짜 Store를 주입할 수 있습니다 👇


🧪 4️⃣ 예시: HomeViewModelTests.swift

import XCTest
import Combine
@testable import LemonLog

// MARK: - Mock Store
@MainActor
final class MockDiaryStore: DiaryProviding {
    var diariesPublisher: AnyPublisher<[EmotionDiaryModel], Never> {
        Just(mockDiaries).eraseToAnyPublisher()
    }

    var snapshot: [EmotionDiaryModel] { mockDiaries }

    var mockDiaries: [EmotionDiaryModel] = []
    var mockImages: [(UIImage?, String)] = []

    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
    }
}

// MARK: - HomeViewModel Test
@MainActor
final class HomeViewModelTests: XCTestCase {
    var cancellables = Set<AnyCancellable>()
    
    func testRecentDiariesLimitToFive() {
        // Given
        let mockStore = MockDiaryStore()
        mockStore.mockDiaries = (1...10).map {
            EmotionDiaryModel(
                id: UUID(),
                emotion: "happy_grade_1",
                content: "테스트 \($0)",
                createdAt: Date().addingTimeInterval(-Double($0) * 100)
            )
        }
        
        // When
        let viewModel = HomeViewModel(store: mockStore)
        
        // Then
        XCTAssertEqual(viewModel.recentDiaries.count, 5)
    }
    
    func testEmotionSummaryCountsProperly() {
        let mockStore = MockDiaryStore()
        mockStore.mockDiaries = [
            EmotionDiaryModel(id: UUID(), emotion: "happy_grade_1", content: "", createdAt: Date()),
            EmotionDiaryModel(id: UUID(), emotion: "sad_grade_1", content: "", createdAt: Date()),
            EmotionDiaryModel(id: UUID(), emotion: "happy_grade_1", content: "", createdAt: Date())
        ]
        
        let viewModel = HomeViewModel(store: mockStore)
        XCTAssertEqual(viewModel.emotionSummary[.happy_grade_1], 2)
        XCTAssertEqual(viewModel.emotionSummary[.sad_grade_1], 1)
    }
}

🧠 5️⃣ 이렇게 하면 좋은 이유

이유 설명
✅ 빠르다 Core Data 접근 없이 테스트 → 즉시 실행
✅ 안정적이다 DB에 의존하지 않으므로 테스트가 실패해도 환경 문제 아님
✅ 독립적이다 ViewModel 로직만 검증 가능
✅ 재사용 가능 다른 ViewModel 테스트에도 Mock 주입 가능

🧩 6️⃣ 결론

HomeViewModel도 XCTest로 테스트해야 합니다.


하지만 Core Data를 직접 쓰는 게 아니라,
DiaryProviding 프로토콜 기반으로 Mock Store를 주입해서 테스트하는 게 정석이에요.

728x90
LIST