Skip to content

에뮬레이터가 필요 없는 androidTest 14개를 feature 모듈 src/test 로 — 위치 때문에 한 번도 실행되지 않는 상태 해소 #1035

Closed
2 of 2 issues completed

Description

@OnlyReadOneBook

배경

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.kt
  • feature/timeletter/presentation/src/test/kotlin/.../screen/sender/TimeLetterWriteScreenTest.kt
  • feature/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 본문에 적는다.

비범위

선행

#1030 이 fake 를 testFixtures 로 접은 뒤에 옮긴다. 순서가 반대면 테스트를 옮기면서 로컬 fake 도 함께 들고 가야 하고, 그 fake 를 다시 걷어내는 두 번째 작업이 같은 파일에 얹힌다.

Activity

  1. OnlyReadOneBook commented on Aug 28, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    ReceiverVerification 파일럿 결과

    • 구현 PR: test: ReceiverVerification을 Robolectric JVM 테스트로 이동 (#1035) #1344
    • 결과: JVM Robolectric 테스트 3/3 통과 (failures=0, errors=0, skipped=0)
    • 환경: RobolectricTestRunner, SDK 35, Native graphics, Compose v2 createComposeRule
    • 제거: instrumentation 전용 FailureArtifactRule (InstrumentationRegistry, logcat, test storage 의존)
    • 대체: app 전용 FakeErrorReporter를 test-local no-op reporter로 교체. reporter 기록은 기존 단언 대상이 아니었습니다.
    • timing/clock 보정: 없음
    • 테스트 함수·시나리오·입력·5초 timeout·assertion 변경: 없음
    • 추가 검증: receiver ktlint, app androidTest 컴파일, 전체 Kover, Konsist 통과

    후속 결정

    파일럿이 별도 timing 보정 없이 안정적으로 통과했으므로 나머지 대상도 v2 createComposeRule + Robolectric SDK 35 + Native 규약으로 feature src/test에 이관합니다. 이슈 표는 총 14개이므로 파일럿 1개를 제외한 후속 대상은 본문의 12개가 아니라 13개입니다.

  2. changed the title [-]test(android): 에뮬레이터가 필요 없는 androidTest 14개를 feature 모듈 src/test 로 — 위치 때문에 한 번도 실행되지 않는 상태 해소[/-] [+]에뮬레이터가 필요 없는 androidTest 14개를 feature 모듈 src/test 로 — 위치 때문에 한 번도 실행되지 않는 상태 해소[/+] on Aug 28, 2026
  3. added
    test테스트·검증·회귀 방지 작업
    area:platformCI·빌드·릴리스·저장소 운영 영역
    and removed
    enhancementNew feature or request
    on Aug 28, 2026
  4. OnlyReadOneBook commented on Aug 28, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    Afternote·Onboarding 후속 이관 결과

    이관 보류 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 본문에도 남겼습니다.

  5. added 2 commits that reference this issue on Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:platformCI·빌드·릴리스·저장소 운영 영역test테스트·검증·회귀 방지 작업

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions