Repository navigation
에뮬레이터가 필요 없는 androidTest 14개를 feature 모듈 src/test 로 — 위치 때문에 한 번도 실행되지 않는 상태 해소 #1035
Description
배경
app/src/androidTest 의 테스트 19개 중 16개는 에뮬레이터가 필요 없다. createComposeRule 로 화면을 띄우고 손으로 만든 fake 를 물려 semantics 를 단언할 뿐, Hilt 앱 그래프도 실제 Activity 도 쓰지 않는다.
그런데 app/src/androidTest 에 있다는 이유로 이것들은 Gradle Managed Device 스위트에 묶여 있고, 그 스위트는 cron: 17 18 * * 2,5 로만 돈다(android-managed-device.yml, PR 트리거 없음 — #835 가 "안정화 후 활성화" 로 의도한 상태다). gh run list --workflow="Android Managed Device Test" 기준 실행 이력 0건이다.
즉 위치가 실행 여부를 결정하고 있다. 같은 코드가 모듈 src/test 에 있었으면 매 PR :koverXmlReportCi 에서 돌았다.
이 레포엔 이미 그 길이 깔려 있다
Robolectric 4.15.1 로 Compose UI 테스트를 JVM 에서 돌리는 게 이미 3개 모듈에서 동작 중이다:
core/ui/src/test/kotlin/com/afternote/core/ui/button/AfternoteButtonTest.ktfeature/timeletter/presentation/src/test/kotlin/.../screen/sender/TimeLetterWriteScreenTest.ktfeature/mindrecord/presentation/src/test/kotlin/.../component/WeeklyReportWeekMenuTest.kt외 2건
새 인프라를 들이는 게 아니라 이미 굴러가는 방식으로 나머지를 옮기는 작업이다.
옮길 대상
@HiltAndroidTest·실제 Activity 를 쓰지 않고, import 상 단일 feature 만 참조하는 14개:
범위 조정 (2026-08-25) — 담당 모듈 경계에 맞춰 설정·타임레터 몫은 #1089, 마음의 기록 몫은 #1090 으로 분리했다. 이 이슈는 파일럿 + 애프터노트·수신자·온보딩 5개를 맡는다.
| 목적지 모듈 | 파일 | 담당 |
|---|---|---|
feature/afternote/presentation |
AfternoteAuthor, AfternoteAuthorExtended, AfternoteAuthorImplementedCoverage |
이 이슈 |
feature/receiver/presentation |
ReceiverVerification |
이 이슈 · 파일럿 |
feature/onboarding/presentation |
OnboardingContract |
이 이슈 |
feature/setting/presentation |
SettingFlow, SettingAccountSecurity, SettingCompletion, ReceiverSelection, ReceiverSelectionBoundary |
#1089 @koongmai |
feature/timeletter/presentation |
TimeLetterFlow, TimeLetterLifecycle |
#1089 @koongmai |
feature/mindrecord/presentation |
MindRecordFlow, MindRecordLifecycle |
#1090 @Sadturtleman |
app 에 남는 것 (5개) — 여기 있는 게 맞다:
AppOnboardingCanaryTest,AppAndReceiverCompletionAndroidTest,SettingImplementedCoverageAndroidTest—@HiltAndroidTest+ 실제 Activity. 실제 앱 그래프·프로세스 재생성·모듈 간 내비게이션 왕복은 app 계층에서만 검증된다.ReceiverAdvancedAndroidTest(afternote+receiver),TimeLetterMindRecordCompletionAndroidTest(mindrecord+timeletter) — 두 기능을 가로지른다. 쪼갤 수 있으면 쪼개고, 아니면 app 에 둔다. 판단은 옮기면서 한다.
한 번에 옮기지 않는다
Robolectric 아래의 Compose 테스트는 애니메이션 진행·waitUntil 타이밍이 실기기와 달라 손봐야 하는 게 나온다. 14개를 한꺼번에 옮기면 어느 실패가 진짜 회귀이고 어느 게 환경 차이인지 가려낼 수 없다.
1단계 — feature/receiver/presentation 파일럿 1개(ReceiverVerification).
당초 파일럿은 timeletter 2개였다. 그 모듈이 담당 분리로 #1089 로 넘어갔고, 파일럿은 규약을 세우는 작업이라 총괄이 직접 맡는 게 맞다 — 다른 담당자가 파일럿을 하면 규약까지 그쪽이 세워야 한다.
feature/receiver/presentation 을 고른 이유:
libs.robolectric이 이미 선언돼 있고 Robolectric 테스트가 2건 돌고 있다- 옮길 파일이 1개뿐이라 시행착오 비용이 가장 싸다
- Compose + Robolectric 조합의 선례는
core/ui/src/test/.../AfternoteButtonTest.kt를 본뜬다
여기서 걸리는 항목(애니메이션 진행·waitUntil 타이밍)을 정리한 뒤 나머지의 이관 여부·순서를 판단한다. #1089 · #1090 은 이 결과를 기다린다.
파일럿에서 Robolectric 이 감당 못 하는 게 드러나면 그 파일은 app 에 남긴다. 전부 옮기는 게 목표가 아니라, 옮길 수 있는 걸 옮겨 PR 마다 돌게 만드는 게 목표다.
완료 조건
- 파일럿 1개가
feature/receiver/presentation/src/test에서:koverXmlReportCi로 실행되고 통과한다. - 옮긴 파일이
app/src/androidTest에서 사라지고,:app:compileDebugAndroidTestKotlin이 여전히 통과한다. - 파일럿 결과(통과·수정 필요·이관 불가)를 항목별로 이 이슈에 남기고, 나머지 12개의 처리 방침을 결정한다.
- 단언은 옮기는 김에 고치지 않는다. 환경 차이로 불가피한 수정만 하고, 그 각각을 이유와 함께 PR 본문에 적는다.
비범위
- GMD 스위트를 PR 필수 체크로 올리는 것 — Gradle Managed Device 기반 전 범위 androidTest 인프라와 app·core·onboarding canary 도입 #835 의 "3회 연속 성공 후 판단" 게이트 소관이다. 이 이슈는 오히려 GMD 에 남길 것을 줄여 그 판단을 쉽게 만든다.
- app 에 남는 5개를 손보는 것.
- 새 테스트 시나리오 추가.
선행
#1030 이 fake 를 testFixtures 로 접은 뒤에 옮긴다. 순서가 반대면 테스트를 옮기면서 로컬 fake 도 함께 들고 가야 하고, 그 fake 를 다시 걷어내는 두 번째 작업이 같은 파일에 얹힌다.
Activity
OnlyReadOneBook commentedon Aug 28, 2026
ReceiverVerification 파일럿 결과
failures=0,errors=0,skipped=0)RobolectricTestRunner, SDK 35, Native graphics, Compose v2createComposeRuleFailureArtifactRule(InstrumentationRegistry, logcat, test storage 의존)FakeErrorReporter를 test-local no-op reporter로 교체. reporter 기록은 기존 단언 대상이 아니었습니다.후속 결정
파일럿이 별도 timing 보정 없이 안정적으로 통과했으므로 나머지 대상도
v2 createComposeRule + Robolectric SDK 35 + Native규약으로 featuresrc/test에 이관합니다. 이슈 표는 총 14개이므로 파일럿 1개를 제외한 후속 대상은 본문의12개가 아니라 13개입니다.[-]test(android): 에뮬레이터가 필요 없는 androidTest 14개를 feature 모듈 src/test 로 — 위치 때문에 한 번도 실행되지 않는 상태 해소[/-][+]에뮬레이터가 필요 없는 androidTest 14개를 feature 모듈 src/test 로 — 위치 때문에 한 번도 실행되지 않는 상태 해소[/+]OnlyReadOneBook commentedon Aug 28, 2026
Afternote·Onboarding 후속 이관 결과
RobolectricTestRunner, SDK 35, Native graphics, Compose v2createComposeRule이관 보류 1개
AfternoteAuthorExtendedAndroidTest#home_loadingErrorRetrySuccess_filterAndRoutesStayConnectedToRepository는 app instrumentation에 유지합니다.실제
Pager의 첫 load를 Error로 전환하는 구간이 Robolectric에서 반복 실행 시 비결정적으로 5초 timeout에 걸렸습니다. 기본 v2/Main, effect dispatcher 격리, Main dispatcher 격리, Looper 배출,flowOn, 정적 Loading 뒤 실제Pager.flow지속 수집을 각각 반복 검증했지만 같은 Error LoadState 전파 경계에서 재현됐습니다.이슈에 정한 경계대로 assertion이나 timeout을 약화하지 않았고, 테스트 본문·
RetryListPagingSource·목록 fixture는 원본 그대로 app에 남겼습니다. 나머지 Extended 3개만 JVM으로 이동했습니다.통합 주의
PR #1334가 먼저 병합되면 Android 파일의 이동된 3개 삭제는 유지하고, 반복 검증 popup 변경을 새 JVM
AfternoteAuthorExtendedTest에 포팅해야 합니다. 이 내용은 #1358 본문에도 남겼습니다.Merge pull request #1344 from Afternote/test/1035-receiver-verificati…
Merge pull request #1358 from Afternote/test/1035-afternote-onboardin…