-
Notifications
You must be signed in to change notification settings - Fork 1
feat: domain model 테스트 코드 작성 #798
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
Changes from 8 commits
6f38b38
994931b
7b07cf8
7329f57
1c06214
97fc602
c352f56
cd6dc59
57822da
9804de8
1c05a89
590f8d5
0bf9893
8b1fc3c
579be2d
0da029c
cd00490
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
|
|
||
| package com.into.websoso.domain.usecase | ||
|
|
||
| import com.into.websoso.data.model.ExploreResultEntity | ||
| import com.into.websoso.data.repository.NovelRepository | ||
| import io.mockk.clearMocks | ||
| import io.mockk.coEvery | ||
| import io.mockk.coVerify | ||
| import io.mockk.mockk | ||
| import kotlinx.coroutines.test.runTest | ||
| import org.junit.Before | ||
| import org.junit.Test | ||
|
|
||
| class GetSearchedNovelsUseCaseTest { | ||
|
|
||
| private lateinit var getSearchedNovelsUseCase: GetSearchedNovelsUseCase | ||
| private val novelRepository: NovelRepository = mockk(relaxed = true) | ||
|
|
||
| private val dummyExploreResultEntity = ExploreResultEntity( | ||
| resultCount = 0L, | ||
| isLoadable = true, | ||
| novels = emptyList() | ||
| ) | ||
|
|
||
| @Before | ||
| fun setUp() { | ||
| getSearchedNovelsUseCase = GetSearchedNovelsUseCase(novelRepository) | ||
| coEvery { novelRepository.fetchNormalExploreResult(any(), any(), any()) } returns dummyExploreResultEntity | ||
| } | ||
|
|
||
| @Test | ||
| fun `처음 검색하면 페이지는 0, 사이즈는 20으로 레포지토리에 요청한다`() = runTest { | ||
|
|
||
| // when | ||
| getSearchedNovelsUseCase("웹소설") | ||
|
|
||
| // then | ||
|
|
||
| coVerify(exactly = 1) { | ||
| novelRepository.fetchNormalExploreResult( | ||
| searchWord = "웹소설", | ||
| page = 0, | ||
| size = 20 | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| fun `같은 검색어로 다시 검색하면 페이지는 1, 사이즈는 10으로 레포지토리에 요청한다`() = runTest { | ||
| // when | ||
| getSearchedNovelsUseCase("웹소설") // 0페이지 20개 | ||
| getSearchedNovelsUseCase("웹소설") // 1페이지 10개 | ||
|
|
||
| // then | ||
| coVerify(exactly = 1) { | ||
| novelRepository.fetchNormalExploreResult( | ||
| searchWord = "웹소설", | ||
| page = 1, | ||
| size = 10 | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| fun `다른 검색어로 검색하면 캐시를 지우고 페이지는 0으로 레포지토리에 요청한다`() = runTest { | ||
| // when | ||
| getSearchedNovelsUseCase("웹소설") // clear 1회 | ||
| clearMocks(novelRepository, answers = false) | ||
| getSearchedNovelsUseCase("새로운 웹소설") | ||
|
|
||
|
|
||
| // then | ||
| coVerify(exactly = 1) { | ||
| novelRepository.clearCachedNormalExploreResult() | ||
| novelRepository.fetchNormalExploreResult( | ||
| searchWord = "새로운 웹소설", | ||
| page = 0, | ||
| size = 20 | ||
| ) | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,4 +11,5 @@ android { | |
| dependencies { | ||
| implementation(projects.core.common) | ||
| implementation(projects.data.library) | ||
| testImplementation(libs.junit) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. c: 앞으로 테스트를 어느범위까지 적용시킬 생각이신가요?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재는 도메인 레이어 모델과 하나의 UseCase에 대해서만 로컬 유닛 테스트를 작성한 상태입니다. |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| package com.into.websoso.domain.library.model | ||
|
|
||
| import com.into.websoso.domain.library.model.AttractivePoints.Companion.toAttractivePoints | ||
| import org.junit.Assert.* | ||
| import org.junit.Test | ||
|
|
||
| class AttractivePointsTest { | ||
|
|
||
| @Test | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. c: 이 아래로 gwt주석이 없는데, 유즈케이스 테스트랑 코드 스타일이 통일되면 좋을 것 같아요 주석이 다 있거나 아예 다 없거나!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 주석 없는 테스트 케이스 모두 gwt으로 통일하였습니다! |
||
| fun `기본 상태에서는 아무 항목도 선택되지 않는다`() { | ||
| val points = AttractivePoints() | ||
|
|
||
| assertFalse(points.isSelected) | ||
| assertTrue(points.selectedAttractivePoints.isEmpty()) | ||
| assertTrue(points.selectedLabels.isEmpty()) | ||
| assertTrue(points.selectedKeys.isEmpty()) | ||
|
|
||
| for (point in AttractivePoint.entries) { | ||
| assertFalse(points[point]) | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| fun `항목을 선택하면 해당 항목만 선택 상태가 된다`() { | ||
| val points = AttractivePoints() | ||
| .set(AttractivePoint.WORLDVIEW) | ||
|
|
||
| assertTrue(points[AttractivePoint.WORLDVIEW]) | ||
| assertTrue(points.isSelected) | ||
| assertEquals(listOf("세계관"), points.selectedLabels) | ||
| assertEquals(listOf("worldview"), points.selectedKeys) | ||
| } | ||
|
|
||
| @Test | ||
| fun `같은 항목을 다시 선택하면 선택이 해제된다`() { | ||
| val points = AttractivePoints() | ||
| .set(AttractivePoint.WORLDVIEW) | ||
| .set(AttractivePoint.WORLDVIEW) | ||
|
|
||
| assertFalse(points[AttractivePoint.WORLDVIEW]) | ||
| assertFalse(points.isSelected) | ||
| } | ||
|
|
||
| @Test | ||
| fun `문자열 키 목록을 통해 선택 상태를 생성할 수 있다`() { | ||
| val keys = listOf("worldview", "character", "unknown") | ||
|
|
||
| val points = keys.toAttractivePoints() | ||
|
|
||
| assertTrue(points[AttractivePoint.WORLDVIEW]) | ||
| assertTrue(points[AttractivePoint.CHARACTER]) | ||
| assertFalse(points[AttractivePoint.MATERIAL]) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package com.into.websoso.domain.library.model | ||
|
|
||
| import org.junit.Assert.assertEquals | ||
| import org.junit.Assert.assertFalse | ||
| import org.junit.Assert.assertTrue | ||
| import org.junit.Test | ||
|
|
||
| class NovelRatingTest { | ||
|
|
||
| @Test | ||
| fun `기본 평점은 선택되지 않은 상태이다`() { | ||
| val rating = NovelRating() | ||
|
|
||
| assertEquals(Rating.DEFAULT, rating.rating) | ||
| assertFalse(rating.isSelected) | ||
| } | ||
|
|
||
| @Test | ||
| fun `평점을 설정하면 선택 상태가 된다`() { | ||
| val rating = NovelRating.from(4.0f) | ||
|
|
||
| assertTrue(rating.isSelected) | ||
| assertEquals(Rating.FOUR, rating.rating) | ||
| } | ||
|
|
||
| @Test | ||
| fun `같은 평점을 다시 설정하면 기본 상태로 돌아간다`() { | ||
| val rating = NovelRating.from(3.0f) | ||
| .set(Rating.THREE) | ||
|
|
||
| assertEquals(Rating.DEFAULT, rating.rating) | ||
| assertFalse(rating.isSelected) | ||
| } | ||
|
|
||
| @Test | ||
| fun `평점은 근사값 비교로 판단된다`() { | ||
| val rating = NovelRating.from(4.0f) | ||
|
|
||
| assertTrue(rating.isCloseTo(Rating.from(4.00001f))) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,29 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| package com.into.websoso.domain.library.model | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| import org.junit.Assert.assertEquals | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.junit.Test | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| class RatingTest { | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| @Test | ||||||||||||||||||||||||||||||||||||||||||||||
| fun `정확한 값은 대응되는 평점으로 변환된다`() { | ||||||||||||||||||||||||||||||||||||||||||||||
| assertEquals(Rating.DEFAULT, Rating.from(0.0f)) | ||||||||||||||||||||||||||||||||||||||||||||||
| assertEquals(Rating.ONE, Rating.from(1.0f)) | ||||||||||||||||||||||||||||||||||||||||||||||
| assertEquals(Rating.THREE_POINT_FIVE, Rating.from(3.5f)) | ||||||||||||||||||||||||||||||||||||||||||||||
| assertEquals(Rating.FOUR_POINT_EIGHT, Rating.from(4.8f)) | ||||||||||||||||||||||||||||||||||||||||||||||
| assertEquals(Rating.FIVE, Rating.from(5.0f)) | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| @Test | ||||||||||||||||||||||||||||||||||||||||||||||
| fun `근사값도 가까운 평점으로 변환된다`() { | ||||||||||||||||||||||||||||||||||||||||||||||
| assertEquals(Rating.FOUR, Rating.from(4.00001f)) | ||||||||||||||||||||||||||||||||||||||||||||||
| assertEquals(Rating.FOUR_POINT_EIGHT, Rating.from(4.79999f)) | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| @Test | ||||||||||||||||||||||||||||||||||||||||||||||
| fun `매칭되지 않는 값은 기본 평점으로 처리된다`() { | ||||||||||||||||||||||||||||||||||||||||||||||
| assertEquals(Rating.DEFAULT, Rating.from(-1.0f)) | ||||||||||||||||||||||||||||||||||||||||||||||
| assertEquals(Rating.DEFAULT, Rating.from(4.7f)) | ||||||||||||||||||||||||||||||||||||||||||||||
| assertEquals(Rating.DEFAULT, Rating.from(100.0f)) | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+36
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Line 37의 🛠️ 수정 제안 fun `매칭되지 않는 값은 기본 평점으로 처리된다`() {
-// when
+ // when
val negative = Rating.from(-1.0f)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| package com.into.websoso.domain.library.model | ||
|
|
||
| import com.into.websoso.domain.library.model.ReadStatuses.Companion.toReadStatuses | ||
| import org.junit.Assert.* | ||
| import org.junit.Test | ||
|
|
||
| class ReadStatusesTest { | ||
|
|
||
| @Test | ||
| fun `기본 상태에서는 아무 상태도 선택되지 않는다`() { | ||
| val statuses = ReadStatuses() | ||
|
|
||
| assertFalse(statuses.isSelected) | ||
| assertTrue(statuses.selectedKeys.isEmpty()) | ||
| assertTrue(statuses.selectedLabels.isEmpty()) | ||
|
|
||
| for (status in ReadStatus.entries) { | ||
| assertFalse(statuses[status]) | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| fun `읽기 상태를 선택하면 해당 상태가 선택된다`() { | ||
| val statuses = ReadStatuses() | ||
| .set(ReadStatus.WATCHING) | ||
|
|
||
| assertTrue(statuses[ReadStatus.WATCHING]) | ||
| assertTrue(statuses.isSelected) | ||
| assertEquals(listOf("WATCHING"), statuses.selectedKeys) | ||
| } | ||
|
|
||
| @Test | ||
| fun `같은 읽기 상태를 다시 선택하면 선택이 해제된다`() { | ||
| val statuses = ReadStatuses() | ||
| .set(ReadStatus.WATCHING) | ||
| .set(ReadStatus.WATCHING) | ||
|
|
||
| assertFalse(statuses[ReadStatus.WATCHING]) | ||
| assertFalse(statuses.isSelected) | ||
| } | ||
|
|
||
| @Test | ||
| fun `문자열 키 목록으로 읽기 상태를 생성할 수 있다`() { | ||
| val keys = listOf("WATCHING", "QUIT", "UNKNOWN") | ||
|
|
||
| val statuses = keys.toReadStatuses() | ||
|
|
||
| assertTrue(statuses[ReadStatus.WATCHING]) | ||
| assertTrue(statuses[ReadStatus.QUIT]) | ||
| assertFalse(statuses[ReadStatus.WATCHED]) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.into.websoso.domain.library.model | ||
|
|
||
| import org.junit.Assert.assertEquals | ||
| import org.junit.Test | ||
|
|
||
| class SortCriteriaTest { | ||
|
|
||
| @Test | ||
| fun `정렬 키가 일치하면 해당 정렬 기준을 반환한다`() { | ||
| assertEquals(SortCriteria.RECENT, SortCriteria.from("RECENT")) | ||
| assertEquals(SortCriteria.OLD, SortCriteria.from("OLD")) | ||
| } | ||
|
|
||
| @Test | ||
| fun `정렬 키가 일치하지 않으면 최신순으로 처리된다`() { | ||
| assertEquals(SortCriteria.RECENT, SortCriteria.from("UNKNOWN")) | ||
| assertEquals(SortCriteria.RECENT, SortCriteria.from("")) | ||
| assertEquals(SortCriteria.RECENT, SortCriteria.from("recent")) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,7 @@ paging = "3.3.6" | |
| junit = "4.13.2" | ||
| androidx-test-junit = "1.3.0" | ||
| espresso-core = "3.7.0" | ||
| mockk = "1.13.10" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: For Maven Central artifact
The same latest version ( Sources: [1] [2] [3] [4] 🌐 Web query:
💡 Result: MockK 1.14.9 doesn’t introduce Android-specific API breakage, but it can break Android unit-test builds in a couple of practical ways:
testImplementation("junit:junit:4.13.2") // if using MockKRule / JUnit4
// or
testImplementation("org.junit.jupiter:junit-jupiter:5.x.x") // if using MockKExtension / JUnit5(Use the appropriate one for your test framework.)
Sources: Citations:
🌐 Web query:
💡 Result: MockK 1.14.x — notable “breaking” / migration-relevant changes
Sources: GitHub releases / PRs for MockK. [1] [2] [3] MockK를 1.14.9로 업그레이드 시 JUnit 의존성 선언 필수 최신 버전 1.14.9는 Maven Central에서 2026년 1월에 릴리스되었습니다. 다만 이 버전으로 업그레이드할 때는 다음의 breaking change를 고려해야 합니다:
업그레이드 시 testImplementation("junit:junit:4.13.2") // JUnit 4 사용 시
// 또는
testImplementation("org.junit.jupiter:junit-jupiter:5.x.x") // JUnit 5 사용 시이외에도 1.14.x 버전에서는 설정 파일 위치 변경( ♻️ 버전 업그레이드 제안-mockk = "1.13.10"
+mockk = "1.14.9"🤖 Prompt for AI Agents |
||
|
|
||
| # Networking Libraries | ||
| retrofit = "3.0.0" | ||
|
|
@@ -111,6 +112,7 @@ paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "pag | |
| junit = { module = "junit:junit", version.ref = "junit" } | ||
| androidx-test-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-test-junit" } | ||
| espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso-core" } | ||
| mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" } | ||
|
|
||
| # Networking Libraries | ||
| retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } | ||
|
|
||
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.
a: 어떤 옵션인가요?
Uh oh!
There was an error while loading. Please reload this page.
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.
당시에 로컬 unit test에서 Android 플랫폼 API를 건드리는 코드가 있어서, 예외로 테스트가 터지니까 일단 기본값으로 바꿔서 테스트를 돌리려고 임의로 넣어뒀던 설정입니다.
또한 옵션은 null 혹은 0으로 흘러가면서 버그가 가려질 수 있어서 해당 옵션 삭제하였습니다!
9804de8
https://developer.android.com/training/testing/local-tests#error