From 94c906a065c9e068b338d6921b0229cd3921b831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=BC=ED=98=81?= Date: Fri, 28 Aug 2026 19:05:46 +0900 Subject: [PATCH] test: move mindrecord tests to JVM --- .../presentation/MindRecordFlowTest.kt | 62 +++++++++++-------- .../presentation/MindRecordLifecycleTest.kt | 36 +++++------ 2 files changed, 53 insertions(+), 45 deletions(-) rename app/src/androidTest/java/com/afternote/afternote_fe/MindRecordFlowAndroidTest.kt => feature/mindrecord/presentation/src/test/kotlin/com/afternote/feature/mindrecord/presentation/MindRecordFlowTest.kt (84%) rename app/src/androidTest/java/com/afternote/afternote_fe/MindRecordLifecycleAndroidTest.kt => feature/mindrecord/presentation/src/test/kotlin/com/afternote/feature/mindrecord/presentation/MindRecordLifecycleTest.kt (96%) diff --git a/app/src/androidTest/java/com/afternote/afternote_fe/MindRecordFlowAndroidTest.kt b/feature/mindrecord/presentation/src/test/kotlin/com/afternote/feature/mindrecord/presentation/MindRecordFlowTest.kt similarity index 84% rename from app/src/androidTest/java/com/afternote/afternote_fe/MindRecordFlowAndroidTest.kt rename to feature/mindrecord/presentation/src/test/kotlin/com/afternote/feature/mindrecord/presentation/MindRecordFlowTest.kt index a157fe5c66..1c196aee33 100644 --- a/app/src/androidTest/java/com/afternote/afternote_fe/MindRecordFlowAndroidTest.kt +++ b/feature/mindrecord/presentation/src/test/kotlin/com/afternote/feature/mindrecord/presentation/MindRecordFlowTest.kt @@ -1,24 +1,20 @@ -package com.afternote.afternote_fe +package com.afternote.feature.mindrecord.presentation -import androidx.compose.ui.graphics.asAndroidBitmap import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.captureToImage import androidx.compose.ui.test.hasClickAction import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.junit4.v2.createComposeRule import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.test.onRoot import androidx.compose.ui.test.performClick import androidx.lifecycle.SavedStateHandle -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.afternote.afternote_fe.test.FailureArtifactRule -import com.afternote.afternote_fe.test.FakeErrorReporter -import com.afternote.afternote_fe.test.appTestUserRepository import com.afternote.core.domain.testing.FakePhotoUploadRepository +import com.afternote.core.domain.testing.FakeUserRepository +import com.afternote.core.model.user.Receiver import com.afternote.core.ui.theme.AfternoteTheme import com.afternote.feature.mindrecord.domain.model.TodayMood import com.afternote.feature.mindrecord.domain.testing.FakeDailyQuestionRepository import com.afternote.feature.mindrecord.domain.testing.FakeDiaryRepository +import com.afternote.feature.mindrecord.presentation.reporting.RecordingErrorReporter import com.afternote.feature.mindrecord.presentation.screen.sender.DailyQuestionWriteScreen import com.afternote.feature.mindrecord.presentation.screen.sender.DiaryWriteScreen import com.afternote.feature.mindrecord.presentation.viewmodel.DailyQuestionWriteViewModel @@ -30,18 +26,17 @@ import org.junit.Assert.assertFalse import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class MindRecordFlowAndroidTest { - @get:Rule(order = 0) +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode + +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(sdk = [35]) +class MindRecordFlowTest { + @get:Rule val composeRule = createComposeRule() - @get:Rule(order = 1) - val failureArtifactRule = - FailureArtifactRule { - composeRule.onRoot().captureToImage().asAndroidBitmap() - } - @Test fun diaryFailureThenRetry_preservesInputAndSendsExactPayload() { val repository = FakeDiaryRepository() @@ -54,7 +49,7 @@ class MindRecordFlowAndroidTest { ), ) repository.onCreate = { createResults.removeFirst() } - val reporter = FakeErrorReporter() + val reporter = RecordingErrorReporter() val viewModel = diaryViewModel(repository, reporter) var successCalls = 0 @@ -95,7 +90,7 @@ class MindRecordFlowAndroidTest { // 실패는 한 번뿐이었으므로 기록도 한 번이다. 성공한 재시도가 더 남기면 Crashlytics // 보관 한도(최근 8건)를 잡음으로 채운다 (#964). - assertEquals(listOf("diary_submit"), reporter.mindRecordStages) + assertEquals(listOf("diary_submit"), reporter.stages) } @Test @@ -128,7 +123,7 @@ class MindRecordFlowAndroidTest { ), ) repository.onCreate = { createResults.removeFirst() } - val reporter = FakeErrorReporter() + val reporter = RecordingErrorReporter() val viewModel = DailyQuestionWriteViewModel( savedStateHandle = SavedStateHandle(emptyMap()), @@ -158,7 +153,7 @@ class MindRecordFlowAndroidTest { // 실제 화면을 태우므로 에디터가 되돌려 주는 HTML 이 그대로 상태에 들어간다 — // 종전 테스트는 화면을 건너뛰어 원문 문자열이 남았다. assertEquals("

오늘은 용기 냈다

", viewModel.uiState.value.answer) - assertEquals(listOf("daily_question_submit"), reporter.mindRecordStages) + assertEquals(listOf("daily_question_submit"), reporter.stages) composeRule.runOnIdle { viewModel.consumeSubmitResult() } composeRule.onNode(hasText("저장") and hasClickAction()).performClick() @@ -171,12 +166,12 @@ class MindRecordFlowAndroidTest { assertEquals("

오늘은 용기 냈다

", repository.createdPayloads.last().content) assertFalse(repository.createdPayloads.last().isDraft) // 성공한 재시도는 기록을 늘리지 않는다. - assertEquals(listOf("daily_question_submit"), reporter.mindRecordStages) + assertEquals(listOf("daily_question_submit"), reporter.stages) } private fun diaryViewModel( repository: FakeDiaryRepository, - reporter: FakeErrorReporter = FakeErrorReporter(), + reporter: RecordingErrorReporter = RecordingErrorReporter(), ): DiaryWriteViewModel = DiaryWriteViewModel( savedStateHandle = @@ -192,8 +187,23 @@ class MindRecordFlowAndroidTest { uploadedUrl = "https://cdn.test/image.jpg", uploadedKey = "mindrecords/1/image.jpg", ), - userRepository = appTestUserRepository(), + userRepository = mindRecordFlowUserRepository(), draftLoader = MindRecordDraftLoader(repository, FakeDailyQuestionRepository()), errorReporter = reporter, ) } + +private fun mindRecordFlowUserRepository(): FakeUserRepository = + FakeUserRepository.strict().apply { + receiverState.value = listOf(Receiver(7L, "김수신", "가족", "fake-auth-7")) + onReceiverListFlow = null + onGetReceivers = null + onCreateReceiver = null + onGetMyProfile = null + onUpdateMyProfile = null + onDeleteAccount = null + onLogActivity = null + onGetMyPushSettings = null + onUpdateMyPushSettings = null + onGetConnectedAccounts = null + } diff --git a/app/src/androidTest/java/com/afternote/afternote_fe/MindRecordLifecycleAndroidTest.kt b/feature/mindrecord/presentation/src/test/kotlin/com/afternote/feature/mindrecord/presentation/MindRecordLifecycleTest.kt similarity index 96% rename from app/src/androidTest/java/com/afternote/afternote_fe/MindRecordLifecycleAndroidTest.kt rename to feature/mindrecord/presentation/src/test/kotlin/com/afternote/feature/mindrecord/presentation/MindRecordLifecycleTest.kt index aae8df3c8b..2290395309 100644 --- a/app/src/androidTest/java/com/afternote/afternote_fe/MindRecordLifecycleAndroidTest.kt +++ b/feature/mindrecord/presentation/src/test/kotlin/com/afternote/feature/mindrecord/presentation/MindRecordLifecycleTest.kt @@ -1,18 +1,12 @@ -package com.afternote.afternote_fe +package com.afternote.feature.mindrecord.presentation -import androidx.compose.ui.graphics.asAndroidBitmap import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.captureToImage import androidx.compose.ui.test.hasClickAction import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.junit4.v2.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.test.onRoot import androidx.compose.ui.test.performClick -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.afternote.afternote_fe.test.FailureArtifactRule -import com.afternote.afternote_fe.test.FakeErrorReporter import com.afternote.core.domain.testing.FakeUserRepository import com.afternote.core.model.user.User import com.afternote.core.ui.theme.AfternoteTheme @@ -38,6 +32,7 @@ import com.afternote.feature.mindrecord.domain.testing.FakeWeeklyReportRepositor import com.afternote.feature.mindrecord.domain.testing.unexpectedCall import com.afternote.feature.mindrecord.presentation.model.DayBackground import com.afternote.feature.mindrecord.presentation.model.DayContent +import com.afternote.feature.mindrecord.presentation.reporting.RecordingErrorReporter import com.afternote.feature.mindrecord.presentation.screen.receiver.ReceiverMindRecordScreen import com.afternote.feature.mindrecord.presentation.screen.sender.DiaryScreen import com.afternote.feature.mindrecord.presentation.screen.sender.DraftListScreen @@ -59,21 +54,20 @@ import org.junit.Assert.assertTrue import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode import java.time.DayOfWeek import java.time.LocalDate import java.time.YearMonth -@RunWith(AndroidJUnit4::class) -class MindRecordLifecycleAndroidTest { - @get:Rule(order = 0) +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(sdk = [35], qualifiers = "w360dp-h800dp-xhdpi") +class MindRecordLifecycleTest { + @get:Rule val composeRule = createComposeRule() - @get:Rule(order = 1) - val failureArtifactRule = - FailureArtifactRule { - composeRule.onRoot().captureToImage().asAndroidBitmap() - } - @Test fun diaryList_excludesDraftChangesMonthAndReloadsAfterDelete() { val currentMonth = YearMonth.now() @@ -162,7 +156,7 @@ class MindRecordLifecycleAndroidTest { loader = MindRecordDraftLoader(diaryRepository, dailyQuestionRepository), diaryRepository = diaryRepository, dailyQuestionRepository = dailyQuestionRepository, - errorReporter = FakeErrorReporter(), + errorReporter = RecordingErrorReporter(), ) composeRule.setContent { @@ -302,7 +296,7 @@ class MindRecordLifecycleAndroidTest { ), ), ) - val viewModel = ReceiverMindRecordViewModel(repository, FakeErrorReporter()) + val viewModel = ReceiverMindRecordViewModel(repository, RecordingErrorReporter()) composeRule.setContent { AfternoteTheme { @@ -338,6 +332,10 @@ class MindRecordLifecycleAndroidTest { .onNode(hasText("2026-08-10 - 2026-08-31") and hasClickAction()) .performClick() composeRule.onNodeWithText("초기화").performClick() + composeRule.waitUntil(timeoutMillis = TIMEOUT) { + (viewModel.uiState.value as? ReceiverMindRecordUiState.Success)?.filter?.isApplied == false + } + composeRule.waitForIdle() composeRule.onNodeWithText("2026-08-10 - 2026-08-31").assertDoesNotExist() composeRule.onNodeWithText("범위 밖 일기").assertIsDisplayed() composeRule.onNodeWithText("데일리 질문").performClick()