Golden Test(스냅샷 테스트, 레퍼런스 테스팅) — 현재 컴포넌트 렌더링을 미리 저장된 레퍼런스 이미지(golden 파일)와 비교하는 UI 시각적 테스트 방법입니다. 픽셀 변경이 설정된 임계값을 초과하면 테스트가 실패하고 diff 이미지를 생성합니다. 개발자는 diff를 검토하고 변경 사항을 수락(golden 업데이트)하거나 버그를 수정합니다. 자세한 내용은 Paparazzi에 관한 Meta Engineering 기사를 참조하세요.
핵심 사항
Golden Test — 레퍼런스와 픽셀 단위로 비교하여 컴포넌트의 시각적 외관을 자동으로 확인하는 방법입니다. 프로세스: (1) 개발자 또는 테스터가 컴포넌트의 첫 번째 스냅샷을 만듭니다 — 이것이 “golden”(레퍼런스)입니다. (2) golden 파일은 테스트 옆 리포지토리에 저장됩니다. (3) 이후 실행에서 테스트는 컴포넌트를 다시 렌더링하고 저장된 golden과 비교합니다. (4) 이미지가 일치하면 — 테스트 통과. 다르면 — 테스트 실패 및 diff 표시. 결정: 변경 사항이 예상된 경우(golden 업데이트) 또는 버그입니다.
Golden 생성 방법 — 라이브러리는 실제 디스플레이 없이 오프스크린 버퍼(Android: Canvas, iOS: UIGraphicsImageRenderer)에 컴포넌트를 렌더링합니다. 즉, golden 테스트는 화면 에뮬레이터(가상 디스플레이) 없이 CI에서 작동하여 실행 속도를 높입니다. Android의 Paparazzi는 Android Studio의 Layoutlib — Layout Editor와 동일한 엔진 — 을 사용합니다. iOSSnapshotTestCase는 CGImage로 UIKit 렌더링을 사용합니다. 결과는 고정 크기의 PNG 파일입니다.
Golden 파일 — 한 화면(1080x1920)의 PNG 스냅샷은 복잡성에 따라 200–800KB를 차지합니다. 500개의 golden 테스트가 있는 프로젝트의 경우 리포지토리에 ~100–400MB입니다. 해결책: (1) golden을 Git LFS에 저장합니다. (2) PNG 압축(pngcrush, oxipng)을 사용합니다. (3) golden을 별도 스토리지(S3)에 저장하고 빌드 시 가져옵니다. IT Sectr에서는 파일당 1MB 임계값으로 Git LFS에 golden을 저장합니다 — 90%의 테스트에 충분합니다.
Flaky golden 테스트 — golden 테스트의 주요 문제입니다. 다른 GPU, 글꼴 버전 및 안티앨리어싱으로 인해 픽셀에 미세한 차이가 발생합니다. 해결책: 임계값(허용 가능한 다른 픽셀 비율), 퍼지 비교, 동일한 CI 에이전트(동일한 GPU, OS, 에뮬레이터 버전)에서 실행. Paparazzi는 픽셀 단위 비교를 사용하므로 CI 에이전트가 동일해야 합니다.
Golden Test — 고정 레퍼런스를 사용하는 스크린샷 테스트 유형입니다. “golden”이라는 용어는 레퍼런스가 팀에 의해 승인되고 리포지토리에 저장되었음을 의미합니다. 이미지 변경에는 개발자의 신중한 결정(golden 업데이트 또는 코드 수정)이 필요합니다. Golden Test는 개별 컴포넌트(Composable, UIView) 수준에서 작동하며 실제 기기가 필요하지 않습니다.
Screenshot Test — 더 넓은 개념입니다. 스크린샷 테스트는 실제 데이터, 탐색, 시스템 상태 표시줄 및 애니메이션을 포함한 전체 화면을 캡처할 수 있습니다. 스크린샷 테스트는 종종 실제 기기나 에뮬레이터에서 UI Automator(Android) 또는 XCUITest(iOS)를 통해 실행됩니다. Golden 테스트는 에뮬레이터 없이 유닛 테스트 환경(JVM, XCTest)에서 실행되며 단일 컴포넌트만 캡처합니다.
| 특성 | Golden Test | Screenshot Test |
|---|---|---|
| 수준 | 컴포넌트/Composable/View | 전체 화면 |
| 환경 | 유닛 테스트(오프스크린 버퍼) | 기기/에뮬레이터 |
| 속도 | 테스트당 50–200ms | 테스트당 2–30초 |
| 애니메이션 | 지원 안 함 | 지원(일시 중지 포함) |
| GPU 없는 CI | 작동(Layoutlib) | 에뮬레이터 필요 |
| 설정 복잡성 | 낮음 | 높음(에뮬레이터/Device Farm) |
| 불안정성 | 중간(다른 GPU) | 높음(에뮬레이터, 시간) |
Golden vs Screenshot — 각 커밋에서 개별 UI 컴포넌트(버튼, 카드, 대화상자)를 확인하는 golden 테스트. 릴리스 전에 전체 화면의 E2E 확인을 위한 스크린샷 테스트. Golden 테스트는 개발자에게 빠른 피드백을 제공하고, 스크린샷 테스트는 전체 애플리케이션의 무결성에 대한 신뢰를 제공합니다. IT Sectr에서는 Pull Request에 golden 테스트(3–5분)를 사용하고, 야간에 스크린샷 테스트(30–60분)를 사용합니다.
Paparazzi — Cash App(Square)의 라이브러리로, Android View 및 Jetpack Compose 컴포넌트를 에뮬레이터 없이 PNG로 렌더링합니다. Layoutlib(Android Studio Preview와 동일한 엔진)을 사용합니다. 설정: Gradle 플러그인 추가, @Test 및 @RunWith(PaparazziRule::class)로 테스트 작성, paparazzi.snapshot(view) 호출. Paparazzi는 애니메이션, 비디오 및 Real Device를 지원하지 않습니다 — 정적 컴포넌트 렌더링만 가능합니다.
// build.gradle.kts (module)
plugins {
id("app.cash.paparazzi") version "1.3.1"
}
// Compose 컴포넌트용 Golden test
class ButtonGoldenTest {
@get:Rule
val paparazzi = Paparazzi(
Paparazzi.PaparazziSnapshotConfig(
deviceConfig = DeviceConfig.PIXEL_6,
theme = "android:Theme.Material.Light.NoActionBar"
)
)
@Test
fun primary_button() {
paparazzi.snapshot {
Button(
onClick = { },
modifier = Modifier.width(200.dp)
) {
Text("Submit")
}
}
}
}
Roborazzi — Compose, View 및 이미지 비교를 지원하는 Paparazzi의 대안입니다. 차이점: Roborazzi는 Robolectric을 통해 작동하며 임계값(허용 픽셀 차이 비율)을 지원합니다. 이는 CI에서 다른 GPU로 인한 불안정성을 줄입니다. Roborazzi는 변경 사항의 GIF 애니메이션(전/후/diff)도 생성할 수 있어 코드 검토에 편리합니다. 파일 형식: PNG + JSON 메타데이터.
Golden 업데이트 — 의도적인 UI 변경 후 개발자는 이전 golden 파일을 삭제하고 record 플래그로 테스트를 실행합니다. Paparazzi가 모든 golden 파일을 다시 생성합니다. 그런 다음 개발자는 코드 변경과 함께 새 golden 파일을 커밋합니다. 코드 검토에서 검토자는 이전 및 새 golden 파일의 diff를 확인합니다. 변경 사항이 승인되면 — PR이 병합됩니다. 승인되지 않으면 — 개발자가 코드를 수정하고 테스트를 다시 실행합니다. CI에서 golden 파일을 자동으로 업데이트하지 마십시오 — 로컬에서만 수행하세요.
SwiftSnapshotTesting — pointfree.co의 라이브러리, Composable Architecture의 제작자. UIView, UIViewController, CALayer 및 SwiftUI View를 지원합니다. 원칙: assertSnapshot(matching: view, as: .image). 첫 번째 실행에서 golden이 자동으로 생성됩니다. 이후 실행에서 비교됩니다. 차이가 허용 임계값을 초과하면 테스트가 실패합니다. SwiftSnapshotTesting은 UIGraphicsImageRenderer를 통해 작동하며 CI(Xcode Cloud, GitHub Actions)와 호환됩니다.
import SnapshotTesting
import XCTest
final class ProfileCardSnapshotTests: XCTestCase {
func test_profile_card_default() {
let card = ProfileCard(
name: "Alice",
avatar: UIImage.testImage(),
badge: "Pro"
)
let controller = UIHostingController(rootView: card)
assertSnapshot(
matching: controller,
as: .image(on: .iPhoneSe),
record: ProcessInfo.processInfo
.environment["RECORD"] != nil
)
}
}
iOSSnapshotTestCase(이전 FBSnapshotTestCase) — Uber의 UIKit용 라이브러리. SwiftSnapshotTesting과 달리 iOSSnapshotTestCase는 화면 크기와 방향을 지정해야 합니다. Golden 파일은 ReferenceImages 폴더의 PNG입니다. 장점: SwiftUI 없이 UIKit에서 작동하며 iOS 12+를 지원합니다. 단점: golden을 자동으로 업데이트하지 않음 — record 플래그로 실행해야 합니다. SwiftSnapshotTesting이 더 현대적이며 새 프로젝트에 권장됩니다.
기기별 golden — 화면 크기와 방향에 따라 golden 파일이 다릅니다. 표준 방식: golden 파일 이름을 TestName@3x~iPhone14.png로 지정합니다. SwiftSnapshotTesting은 .image(on: .iPhoneSe) 매개변수가 지정된 경우 자동으로 기기 접미사를 추가합니다. Android에서 Paparazzi는 DeviceConfig를 사용하여 크기를 설정합니다. 지원되는 각 기기 폼 팩터에 대해 golden을 별도로 저장하세요. 다른 크기에 하나의 golden을 사용하지 마십시오 — 불안정한 테스트가 발생합니다.
CI 파이프라인 — golden 테스트는 모든 Pull Request에서 실행되어야 합니다. 테스트가 실패하면 CI는 diff 이미지를 빌드 아티팩트로 표시합니다. 개발자는 diff를 검토하고 결정을 내립니다. 중요: CI에서 생성된 golden 파일은 자동으로 커밋되지 않습니다. 의도적인 변경 후 개발자가 로컬에서만 생성합니다. GitHub Actions 및 GitLab CI는 브라우저에서 diff를 보기 위한 아티팩트(png, html) 업로드를 지원합니다.
리포지토리 크기 — golden 파일은 빠르게 증가합니다. 500개 테스트 = 100–400MB PNG. 해결책: (1) Git LFS — 각 golden은 LFS에 저장되며 체크아웃 시에만 복제됩니다. (2) golden을 별도 리포지토리에 저장하고 서브모듈로 포함합니다. (3) S3 + 캐싱 — golden을 S3에 두고 CI는 체크섬으로 변경된 파일만 다운로드합니다. IT Sectr에서는 track *.png filter=lfs diff=lfs merge=lfs text=false로 Git LFS를 사용합니다. 로컬에서 golden은 src/test/goldens/에 있습니다.
Golden 코드 검토 — 일반 git diff는 PNG 변경 사항을 표시하지 않습니다. 해결책: (1) GitHub는 클릭 시 PNG 이미지를 엽니다. (2) golden diff가 브라우저에 표시되는 Review Apps를 사용합니다. (3) 전/후/diff 열이 있는 HTML 보고서를 생성합니다. Paparazzi는 세 개의 열(실제, 예상, diff)이 있는 HTML 보고서를 만듭니다. 보고서는 CI 아티팩트에 첨부됩니다. 검토자는 파일을 로컬로 다운로드하지 않고 보고서를 봅니다.
Golden 업데이트 시기 — 신중한 UI 변경 후에만. 글꼴, 색상, 패딩, 아이콘 변경 — golden을 업데이트해야 합니다. 새 버튼 추가, 요소 재배열 — golden을 업데이트해야 합니다. 시각적 외관을 변경하는 버그 수정 — golden을 업데이트해야 합니다. UI 변경 없는 리팩토링 — golden이 변경되지 않아야 합니다. UI 코드 변경 없이 golden이 변경된 경우 — 환경으로 인한 flaky 테스트이므로 CI 에이전트나 종속성 버전에서 원인을 찾으세요.
자주 묻는 질문
Golden Test — 유닛 테스트 환경에서 컴포넌트 수준의 스냅샷 테스트(빠름, 에뮬레이터 불필요). Screenshot Test — 기기 또는 에뮬레이터에서 전체 화면 캡처(느리지만 현실적). Golden은 오프스크린 버퍼로 작동하고, screenshot은 실제 디스플레이로 작동합니다. Golden은 모든 커밋에서 CI에 적합하고, screenshot은 릴리스 전 야간 실행에 적합합니다.
주요 원인: (1) CI에서 다른 GPU — 동일한 CI 에이전트를 사용하세요. (2) 다른 글꼴 버전 — OS 버전을 고정하세요. (3) 다른 안티앨리어싱 — 임계값을 설정하세요(Roborazzi, iOSSnapshotTestCase). (4) 애니메이션 — 테스트에서 애니메이션을 비활성화하세요. (5) 시스템 요소(상태 표시줄) — 베젤리스 기기 구성을 사용하세요. Paparazzi는 Layoutlib 덕분에 불안정성에 취약하지 않습니다.
네. Paparazzi는 paparazzi.snapshot { }을 통한 Compose 기본 지원이 있습니다. Roborazzi도 Compose를 지원합니다. iOS에서 SwiftSnapshotTesting은 UIHostingController를 통해 SwiftUI와 함께 작동합니다. Compose 컴포넌트는 Layoutlib을 통해 렌더링되고, SwiftUI는 UIKit 렌더링을 통해 렌더링됩니다. 제한 사항: Compose 및 SwiftUI 애니메이션은 지원되지 않습니다 — golden 테스트는 초기 상태만 캡처합니다.
CI에서 golden 수락을 절대 자동화하지 마세요. 로컬에서만: 개발자가 디렉토리에서 이전 golden 파일을 삭제하고 record 플래그로 테스트를 실행합니다(Paparazzi: record=true, SwiftSnapshotTesting: record=true). Golden 파일이 다시 생성됩니다. 개발자가 각 golden의 정확성을 확인하고 코드와 함께 변경 사항을 커밋합니다. CI에서 자동 수락은 UI 버그를 놓치게 됩니다.
Golden 테스트는 계측 테스트(UI Automator, XCUITest)보다 빠릅니다. 하나의 golden 테스트는 50–200ms에 실행됩니다(Paparazzi: 평균 MacBook Pro에서 100–150ms). 500개의 golden 테스트 = 25–100초. 에뮬레이터를 통한 스크린샷 테스트와 비교: 테스트당 5–30초. Golden 테스트는 빌드를 느리게 하지 않습니다: 100개 테스트 = ~15초로 병합 전 확인에 허용 가능합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.