문서 목차
이 페이지에서목차

Patrol iOS 네이티브 검증 계약

스마트폰에서 **원하는 테스트가 실제로 실행됐는지**, 다음 입력이 **준비되면 즉시 진행하는지**,

스마트폰에서 원하는 테스트가 실제로 실행됐는지, 다음 입력이 준비되면 즉시 진행하는지, 실패했을 때 최초 원인과 정리 결과가 함께 남는지를 확인하는 절차입니다. 기기 조작 전에 QA 환경 위생과 병렬 기기 격리의 소유·변이 계약을 따릅니다.

1. 실패 단계를 먼저 나눈다#

관측판정·다음 확인
기기 unavailable / destination 없음연결·잠금·페어링·Developer Mode 확인. 테스트 미실행
빌드 실패SDK·CLI·Xcode 빌드 로그 확인. 시나리오 실패로 집계하지 않음
Patrol app did not become ready / Executed 0 tests 실제 테스트 진입점·binding 등록 확인. 종료 코드 0이어도 게이트 실패
native 식별자는 맞지만 단정 실패최초 Dart 단정·스택으로 조사. 시나리오 FAIL
실패 후 진단 수집에서 멈춤원래 테스트 실패와 결과 확정 지연을 분리

로그의 마지막 줄이 아니라 처음 실패한 단계를 확인합니다. 연결이 끊겼다면 같은 소스의 완성된 테스트 번들을 test-without-building으로 다시 실행할 수 있습니다. 소스·의존성· 빌드 설정이 바뀌었으면 재빌드하고, 예전 번들 결과를 최신 변경의 증거로 쓰지 않습니다.

2. Generated 설정이 아니라 실제 컴파일 진입점을 확인한다#

flavor target의 고정 FLUTTER_TARGET = lib/main_<flavor>.dart는 Patrol이 생성한 Generated.xcconfig의 integration_test/test_bundle.dart를 덮을 수 있습니다. 번들이 생성됐다는 사실과 그 번들이 컴파일됐다는 사실은 다릅니다.

  1. Generated.xcconfig, Xcode target의 build settings, 실제 Flutter build manifest의 진입점을 대조합니다. 전체 DART_DEFINES나 인증값은 로그에 출력하지 않습니다.
  2. 일반 앱이 컴파일됐다면 테스트 전용 xcconfig에 다음을 둡니다.
       FLUTTER_TARGET = integration_test/test_bundle.dart
    
  3. 해당 build/test 프로세스에만 XCODE_XCCONFIG_FILE로 전달합니다. 일반 flavor의 production 진입점을 지우거나 사용자 전역 환경에 export하지 않습니다.
  4. 새 번들의 실제 컴파일 진입점과 native 테스트 발견 결과를 다시 확인합니다.

CLI 의존성이 앱 workspace의 override와 충돌하면 호환 버전으로 고정한 독립 pubspec에서 CLI를 해석하고 그 package_config.json으로 실행합니다. 실제 앱 빌드는 프로젝트 SDK를 씁니다. 전역 CLI·공유 SDK·앱의 캐시 설정을 바꿔 다른 세션에 영향을 주지 않습니다. 사용한 Flutter/Patrol/CLI/Xcode 버전을 증거에 적고, 한 버전 조합의 성공을 모든 버전의 보장으로 쓰지 않습니다.

RunnerUITests에서 Flutter.framework가 없으면 앱 target과 UITest target의 embed를 각각 확인합니다. 보완은 UITest target에 한정하며, Flutter의 Xcode migration이 거부하는 복사 phase를 반복 추가하지 않습니다. 프로젝트가 지원하는 script embedding과 해당 target의 codesign을 검증합니다.

3. 종료 코드·초록 문구만으로 통과시키지 않는다#

실행 전에 기대하는 **native 테스트 식별자 집합과 N(>0)**을 정합니다. 결과 확정 후 xcresulttool로 요약과 테스트 트리를 모두 읽습니다.

xcrun xcresulttool get test-results summary --path <result.xcresult> --compact
xcrun xcresulttool get test-results tests --path <result.xcresult> --compact

필수 시나리오 게이트는 다음을 함께 확인합니다.

  • totalTestCount == passedTests == N, failedTests == skippedTests == expectedFailures == 0.
  • 실패 목록 없음, 실제 leaf 테스트 수 N, 각 테스트의 통과 결과.
  • 실제 식별자 집합이 기대 집합과 정확히 같음 — 다른 N건·중복·누락을 거부.
  • 이름 정규화는 XCTest가 붙인 클래스 접두사·+ 인코딩·()처럼 확인된 형식만 처리.

요약 필드·트리가 없거나 파싱에 실패하면 미검증/게이트 실패입니다. 0건 성공, 일부 실패, 다른 동일 건수 번들, 올바른 전체 통과를 결과 판정기의 회귀 사례로 유지합니다. 배치에서는 native 1건이 여러 Gherkin 시나리오를 포함하므로 내부 진행 마커·FAIL/BLOCKED/리셋 결과도 확인합니다. native 건수를 Gherkin 건수로 바꾸어 보고하지 않습니다.

실패 후 Xcode 진단 수집이 sudo 입력을 요구해 결과 확정을 막는 경우, 지원되는 Xcode에서 -collect-test-diagnostics never로 추가 sysdiagnose만 끌 수 있습니다. assertion·xcresult· 정확한 식별자 검사를 유지하고, 권한 프롬프트에 자동으로 비밀번호를 넣지 않습니다. 비정상 종료와 확정되지 않은 결과 번들은 여전히 PASS가 아닙니다.

4. 전체 화면 settle 대신 조작 대상의 준비를 기다린다#

커서·스켈레톤·주기적 애니메이션은 계속 프레임을 예약합니다. 화면 전체 pumpAndSettle이나 canonical step의 고정 5초 pump가 필드 준비 조건이 되면 입력 두 번에도 긴 공백이 생깁니다.

단계준비 판정
입력실제 Key 출현 → 실제 scroll/ensureVisible → 가시성 → 대상 route 전환 완료
키보드필요한 전환만 bounded trySettle; 필드마다 불필요하게 닫지 않음
제출실제 폼의 유효/활성 상태를 확인한 즉시 실제 버튼 탭
결과실제 Auth state·사용자 식별자·인증 서비스의 확정 상태 확인

프로젝트의 TestDriver를 확장해 canonical step 호출을 유지하면서 settle 계약을 대상 기반으로 바꿉니다. Key가 사라진 뒤에는 다음 대상의 준비를 확인합니다. 대상 route/keyboard 애니메이션은 계속 처리하고, 시간값은 상한으로 씁니다 — 매번 그 시간만큼 고정 대기하지 않습니다. 짧은 trySettle 이후에도 실제 다음 대상/상태를 확인합니다. 반복 상한 초과는 실패로 남깁니다.

submit 전에 실 credential loading→실패/잠금과 auth loading→확정 전이를 관찰합니다. 이미 실패한 자격증명은 전체 인증 timeout까지 기다리지 말고 바로 실패시킵니다. observer는 finally에서 해제합니다. 입력 로그는 비밀번호·토큰을 출력하지 않도록 끕니다. 로그인 버튼이 사라졌다는 사실을 인증 성공으로 간주하거나, 앱의 인증 시간 예산을 테스트 편의로 연장하지 않습니다.

별도의 비-BDD widget 회귀에서 무관한 지속 애니메이션을 켜고 실제 필드 값·submit 1회· 필요한 준비 시간이 지난 뒤의 즉시 진행을 확인할 수 있습니다. stub 필드·키보드·가짜 인증으로 Patrol의 실제 로그인 시나리오를 대체하지 않습니다.

5. fixture의 복귀와 검증할 복귀를 분리한다#

읽기 전용 데이터를 고르려고 먼저 로그인했다면 fixture가 보호된 탭에서 만든 로그인 복귀도 끝나야 합니다. 활성 flow 종료를 기다리고, 실제 공개 탭을 선택해 route를 단언한 뒤 로그아웃하고 OS 링크를 전달합니다. 이 시작 상태를 Gherkin Background에 명시합니다.

링크 진입 전에 guest 상태·시작 route, 안내에서는 실제 pending request의 owner/origin을 확인합니다. 링크 이후 강제 go()·controller reset·가짜 인증으로 목적지를 맞추지 않습니다. 최종 목적지·query·실제 단일 modal의 내용을 검증합니다. 이전 요청이 남으면 setup 실패로 드러내고, 준비 상태를 몰래 바꿔 시나리오의 전제를 축소하지 않습니다.

warm link·hot restart·완전한 OS cold process는 서로 다릅니다. 선언한 시작 상태의 성공만 보고합니다. 라우팅용 값으로 query 보존을 검증한 것을 실제 쿠폰 할인·결제 검증으로 쓰지 않습니다. 필수 데이터 없음은 skip으로 통과시키지 않습니다.

6. cleanup도 실패시키되 최초 실패·스택을 보존한다#

단순 try { scenario(); } finally { cleanup(); }에서 cleanup까지 throw하면 원래 목적지 단정이 정리 오류로 덮입니다. 최초 예외/스택을 저장하고 cleanup을 별도로 실행합니다.

시나리오cleanup최종 판정
PASSPASSPASS
FAILPASS최초 FAIL·원래 스택
PASSFAILcleanup FAIL
FAILFAIL최초 FAIL·원래 스택, cleanup 실패도 별도 기록

Dart에서는 원래 예외를 Error.throwWithStackTrace로 다시 던질 수 있습니다. 둘 다 실패해도 성공으로 돌리지 않고, 원시 계정/토큰 대신 필요한 오류 유형만 로그에 남깁니다.

실제 취소·뒤로가기 액션으로 modal/login UI를 닫고 로그아웃 전에 잔류·중복 UI 부재를 단언합니다. 로그아웃 후 보호 route guard가 새 login root를 여는 것은 이전 UI 잔류와 다릅니다. 로그아웃은 실제 auth state 확정까지 기다리고 링크 listener·observer를 해제합니다. 다른 세션의 listener·프로세스·기기·캐시를 정리하지 않습니다.

검색어를 입력하면 문서를 찾습니다.

↑ ↓ 이동 · Enter 열기Esc 닫기