ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 골든 테스트 PASS가 실제 화면 증거는 아닙니다
    AI Agent/flutter 2026. 7. 7. 21:00
    728x90
    반응형

    테스트 랙에 꽂힌 폰 세 대와 흐릿한 초록 모니터
    골든은 초록, 실기기는 잘린 두 글자

    CI 화면이 온통 초록이었습니다.
    unit도, widget도, 골든 테스트까지 전부 통과.
    마음 놓고 그날 오후 빌드를 실기기에 올렸습니다.
    그런데 빈 상태 화면을 여니 안내 문구 끝 두 글자가 카드 밖으로 삐져나와 있었습니다.
    "게시물이 아직 없어요" 정도의 짧은 문장이었는데, 오른쪽 모서리에서 잘려 있었습니다.
    다른 기기로 바꿔 껴 봐도 마찬가지였습니다.

    이상했습니다.
    그 화면은 바로 그 오버플로를 막으려고 골든 테스트를 붙여 둔 화면이었거든요.
    테스트는 방금도 초록이라고 했는데, 실기기는 깨졌다고 보여주고 있었습니다.
    테스트 로그를 다시 훑어도 실패한 항목은 없었습니다.
    초록불과 잘린 두 글자를 번갈아 보는데, 둘 중 하나는 분명 거짓말을 하고 있었습니다.
    골든 테스트의 초록불은 "이 위젯이 테스트 환경에서 이렇게 그려진다"를 닫아 줄 뿐, "실기기의 실제 폰트와 실제 화면 폭에서도 괜찮다"를 닫아 주지는 않는다는 걸, 그 잘린 두 글자로 다시 배웠습니다.

    초록불 아래 숨어 있던 폰트

    처음엔 제 레이아웃 코드부터 의심했습니다.
    카드 padding을 줄여도 보고, 텍스트에 maxLinesellipsis를 붙여도 봤습니다.
    그런데 테스트 환경에서는 원래 안 넘쳤으니 고쳐도 티가 안 났고, 실기기에서는 여전히 잘렸습니다.
    한참을 헤매다 baseline 이미지를 열어 실기기 캡처와 나란히 놓고 보니, 그제야 글자 모양이 미묘하게 다른 게 보였습니다.
    자간도, 글자 폭도 조금씩.
    거기서 폰트가 다르다는 걸 눈치챘습니다.
    골든 테스트는 기본적으로 실제 앱 폰트를 로드하지 않습니다.
    폰트를 따로 넣어 주지 않으면 테스트 러너는 대체 폰트로 글자를 그리는데, 이 폰트의 글자 폭과 줄 높이가 실제 기기에 올라가는 폰트와 다릅니다.
    제 baseline은 그 대체 폰트로 찍혀 있었고, 코드도 그 이미지에 맞춰 정직하게 통과하고 있었습니다.

    즉 테스트는 "가짜 폰트 기준으로는 안 넘친다"를 통과하고 있었던 겁니다.
    실제 폰트는 몇 픽셀 더 넓었고, 그 몇 픽셀이 카드 경계에서 두 글자를 밀어냈습니다.
    테스트가 거짓말을 한 게 아니었습니다.
    제가 그 초록불을 다른 질문의 답으로 읽고 있었을 뿐이죠.

    testWidgets('빈 상태 문구가 카드 안에 들어온다', (tester) async {
      // 이 한 줄이 없으면 baseline은 실제 폰트가 아닌 대체 폰트로 찍힙니다.
      await loadAppFonts();
      await tester.pumpWidget(const EmptyStateCard());
      await expectLater(
        find.byType(EmptyStateCard),
        matchesGoldenFile('empty_state_card.png'),
      );
    });
    

    빨간불이 오히려 반가웠던 순간

    loadAppFonts()를 넣고 baseline을 다시 찍으니 골든 테스트가 그제야 오버플로를 잡아냈습니다.
    빨간불이 뜬 순간이 이상하게 반가웠습니다.
    이제야 테스트가 실기기와 같은 걸 보고 있다는 뜻이었으니까요.
    그 자리에서 카드 padding을 몇 픽셀 넉넉히 잡아 다시 돌리니, 실기기에서도 문구가 카드 안으로 들어왔습니다.
    아까 헛되이 손봤던 그 padding이, 이번엔 실제 폰트를 기준으로 잡히니 제대로 먹힌 거죠.

    하지만 여기서 멈추지 않고 하나를 더 인정해야 했습니다.
    폰트를 맞춰도 골든 테스트가 답하는 질문은 여전히 "이 위젯 하나가 이 크기, 이 조건에서 이렇게 그려진다"까지라는 것.
    시스템 폰트 배율을 키운 기기, 가로가 유난히 좁은 기기, 언어를 바꿔 문장이 길어진 화면 — 이건 골든 하나로 닫히는 질문이 아니었습니다.
    각각 다른 테스트거나, 결국 실기기에서 한 번은 눈으로 봐야 하는 것들이었습니다.
    하나의 초록불이 이 전부를 대신 봐 줄 거라고 믿은 게 애초의 착각이었습니다.

    초록불을 믿되, 무엇을 닫아 줬는지까지

    지금은 테스트가 초록이라고 뜨면 "무슨 주장이 닫힌 거지"를 한 번 더 봅니다.
    골든은 렌더 결과를, widget test는 상호작용을, integration test는 흐름을 닫아 줍니다.
    세 테스트가 답하는 질문이 다 다른데, 예전의 저는 초록불 하나를 "다 괜찮다"로 뭉뚱그려 읽고 있었습니다.
    테스트를 못 믿겠다는 뜻은 아닙니다.
    오히려 각 테스트가 무엇까지 책임지는지 알고 나니, 초록불을 볼 때 마음이 더 편해졌습니다.

    누가 "골든도 있는데 실기기를 왜 또 봐요?"라고 물으면, 이제는 그 잘렸던 두 글자 얘기를 합니다.
    테스트가 초록이어도 실제 폰트에서는 넘칠 수 있다고.
    그 한 장면만 얘기하면 대개 고개를 끄덕입니다.
    저도 그 두 글자를 보기 전까진 똑같이 물었을 테니까요.

    그 뒤로 빈 상태 화면 PR에는 실기기 캡처를 한 장 같이 붙이는 습관이 생겼습니다.
    폰트 배율을 조금 키운 채로요.
    골든이 못 보는 자리를, 그 캡처 한 장이 대신 봐 줄 때가 아직도 있어서.

    원문 기준

    원문 기준: https://docs.flutter.dev/testing/overview

    원문 본 날짜: 2026-07-01.

    728x90
    반응형
Designed by Tistory.