MetricKit 진단 데이터를 고정 샘플로 회귀 테스트하기

MetricKit 진단 데이터를 고정 샘플로 회귀 테스트하기

MetricKit 진단 페이로드는 커밋 주기에 맞춰 도착하지 않습니다. 오늘 파서를 한 줄 수정해도 실제 크래시나 멈춤 보고서가 며칠 뒤에야 들어와 필드 누락이 뒤늦게 드러날 수 있습니다. 더 안정적인 방법은 이미 확보한 페이로드에서 민감 정보를 제거하고 정규화한 뒤 고정 샘플로 저장하여, 클라우드 Mac에서 커밋할 때마다 동일한 입력 세트를 재생하는 것입니다. 이렇게 하면 다음 우발적 이벤트를 기다리는 대신 진단 처리 파이프라인 자체를 검증할 수 있습니다.

먼저 테스트 경계 정하기

고정 샘플은 네 가지 계층의 로직을 검증하는 데 적합합니다. 원본 JSON을 읽을 수 있는지, 시스템 필드를 내부 모델에 매핑할 수 있는지, 민감한 내용이 제거되는지, 비정상 입력에서 정상적으로 대체 경로로 전환되는지를 확인할 수 있습니다. 다만 시스템이 실제로 페이로드를 생성한다는 사실까지 입증하지는 못하며, 기기에서 콜백을 검증하는 작업을 대체할 수도 없습니다.

수신 코드와 비즈니스 처리 로직은 분리하는 것이 좋습니다. 수신 계층은 jsonRepresentation()을 가져와 보호된 디렉터리에 기록하고 업로드 대기열에 넣는 역할만 담당합니다. 파싱 계층은 Data를 받아 MetricKit 타입에 의존하지 않는 내부 구조를 출력합니다. 단위 테스트에서는 후자만 호출하여 테스트 타깃에서 시스템 객체를 가짜로 만들 필요가 없도록 합니다.

샘플의 가치는 한 번의 성공적인 파싱을 흉내 내는 데 있지 않습니다. 입력 계약을 고정하여 파서를 수정할 때마다 “어떤 필드가 바뀌었고 어떤 정보가 버려졌는가”에 답할 수 있게 하는 데 있습니다.

저장소에 커밋할 수 있는 정규화 샘플 만들기

원본 페이로드에는 번들 식별자, 기기 정보, 시간, 호출 스택 심벌, 로컬 경로가 포함될 수 있습니다. 이를 그대로 커밋해서는 안 됩니다. 먼저 접근이 제한된 환경에 원본을 보관한 다음, 저장소에 넣을 수 있는 정규화 사본을 생성합니다. 시간은 고정값으로, 식별자는 테스트용 값으로 바꾸고 경로는 $APP, $HOME으로 치환합니다. 호출 스택 주소는 형식만 유지하고 실제 주소는 남기지 않습니다.

내부 형식에는 schema를 추가할 수 있지만, 원본 MetricKit 버전을 변경해서는 안 됩니다. 디렉터리는 진단 유형별로 구성합니다.

Tests/Fixtures/MetricKit/
├── crash/basic.json
├── crash/missing-stack.json
├── hang/main-thread.json
├── disk-write/threshold.json
└── malformed/truncated.json

각 샘플은 하나의 조건만 표현해야 합니다. 한 파일에 크래시, 멈춤, 디스크 이상이 모두 들어 있으면 실패 원인을 찾기 어렵습니다. 샘플 이름은 예상 결과가 아니라 입력을 설명해야 합니다. 예상값은 테스트 코드에 두어야 리뷰할 때 단언문이 무심코 함께 수정되는 일을 더 쉽게 발견할 수 있습니다.

테스트 실행 전에 구조 게이트 적용하기

테스트를 컴파일하기 전에 jq로 비용이 적은 검사를 수행하면 잘못된 JSON, 누락된 버전, 민감 정보가 제거되지 않은 경로를 빠르게 차단할 수 있습니다. 아래의 diagnostics는 팀에서 정의한 정규화 배열이며, 시스템의 원본 페이로드도 같은 구조라고 가정하는 것이 아닙니다.

set -euo pipefail

root="Tests/Fixtures/MetricKit"

find "$root" -name '*.json' -print0 |
while IFS= read -r -d '' file; do
  jq -e '
    type == "object" and
    .schema == 1 and
    (.diagnostics | type == "array") and
    all(.diagnostics[];
      (.kind | type == "string") and
      (.timestamp | type == "string") and
      (.stackID | type == "string")
    )
  ' "$file" >/dev/null

  if grep -E '/Users/|/private/var/|[A-F0-9]{16,}' "$file"; then
    echo "fixture contains unnormalized data: $file" >&2
    exit 1
  fi
done

구조 게이트에서 모든 시스템 필드를 요구해서는 안 됩니다. 그렇게 하면 선택 필드가 새로 추가될 때 의미 없는 실패가 발생합니다. 내부 처리에서 실제로 의존하는 키만 검사하고, 디코더는 알 수 없는 필드를 무시하도록 구성합니다.

정상·오류 샘플로 파서 계약 검증하기

최소한 정상 입력 한 세트와 실패 입력 세 세트를 준비해야 합니다. 테스트의 핵심은 단순히 “오류가 발생하지 않는다”가 아니라, 출력이 집계와 알림, 문제 조사에 계속 활용될 수 있는지를 확인하는 것입니다.

샘플 예상 동작 발생해서는 안 되는 동작
완전한 크래시 유형, 시간, 스택 식별자 출력 원본 로컬 경로 저장
빈 진단 배열 빈 결과 반환 디코딩 실패로 처리
호출 스택 누락 불완전한 데이터로 표시 빈 스택을 정상 데이터로 위조
잘린 JSON 분류 가능한 오류 반환 프로세스 즉시 종료
알 수 없는 유형 알 수 없는 열거형으로 기록 전체 페이로드 묶음 폐기

전체 JSON이 아닌 내부 모델 단언하기

전체 스냅샷은 필드 순서와 무관한 메타데이터 변화에도 쉽게 영향을 받습니다. 진단 개수, 유형, 안정적인 식별자, 민감 정보 제거 결과를 우선 단언해야 합니다. 정규화된 출력을 시스템 간 교환에 사용할 때만 서식화된 JSON 스냅샷을 추가합니다. 오류 역시 비교 가능한 열거형으로 정의해야 합니다. 예를 들어 invalidJSON, missingRequiredField, unsupportedDiagnostic처럼 안정적인 값을 사용하고, 변경되기 쉬운 자연어 메시지만 비교해서는 안 됩니다.

클라우드 Mac CI에 통합하기

VMRunner의 클라우드 Mac에서는 fixture 검사를 단위 테스트보다 먼저 실행하고, 비대화형 작업이 고정된 작업 디렉터리를 사용하도록 해야 합니다. 실행 순서는 코드 체크아웃, 구조 게이트 실행, 파서 단위 테스트 수행, 테스트 결과 생성, 작업 공간에 커밋되지 않은 샘플 변경이 생겼는지 최종 확인 순으로 구성할 수 있습니다.

샘플 변경은 반드시 별도로 리뷰해야 합니다. 새로운 시스템 필드가 추가되면 먼저 파서에서 해당 필드를 사용해야 하는지 확인합니다. 필요하다면 내부 schema를 올리고 마이그레이션 테스트도 함께 커밋합니다. 필요하지 않다면 관대한 디코딩을 유지합니다. CI에서 스크립트로 기준 파일을 자동 덮어쓰지 마십시오. 실제 필드 누락이 새로 생성된 잘못된 결과에 의해 “승인”될 수 있습니다.

병합 전 체크리스트

이러한 제약을 적용하면 MetricKit 진단 처리는 “데이터가 도착한 뒤에야 시험해 보는” 방식에서 벗어나 일반적이고 반복 가능한 엔지니어링 테스트가 됩니다. 시스템 페이로드는 여전히 기기에서 검증해야 하지만, 파싱과 민감 정보 제거, 호환성 검증은 더 이상 우연히 도착하는 보고서에 의존하지 않습니다.

자주 묻는 질문

MetricKit 고정 샘플이 실제 기기 검증을 대신할 수 있나요?

아닙니다. 고정 샘플은 디코딩, 정규화, 민감 정보 제거 로직을 검증하지만 운영체제가 진단 페이로드를 실제로 전달하는지는 기기 테스트와 운영 관측으로 별도 확인해야 합니다.

원본 MetricKit JSON을 저장소에 바로 넣어도 되나요?

권장하지 않습니다. 원본은 접근이 제한된 위치에 보관하고 사용자 식별자, 로컬 경로, 번들 내부 정보가 제거된 정규화 샘플만 저장소에 포함하는 편이 안전합니다.

알 수 없는 새 필드가 생기면 CI를 실패시켜야 하나요?

일반적으로 새 선택 필드는 허용해야 합니다. 다만 내부 계약에서 필수로 정한 진단 종류, 시간, 호출 스택 식별자가 사라지거나 형식이 바뀌면 CI를 실패시켜야 합니다.

전용 물리 노드

다음 빌드를 전용 클라우드 Mac에서 실행하세요

기종, 노드와 결제 주기를 선택하세요. 구성과 달러 금액은 주문 전에 모두 안내되며, 이용 가능 여부는 콘솔에 실시간으로 표시되는 정보를 기준으로 합니다.

요금제 선택 및 주문