Golden Test (snapshot-тест, еталонне тестування) — метод візуального тестування UI, при якому поточний рендер компонента порівнюється з попередньо збереженим еталонним зображенням (golden-файлом). Якщо піксельні зміни перевищують заданий поріг, тест падає та генерує diff-зображення. Розробник переглядає diff та або приймає зміни (оновлює golden), або виправляє баг. Докладніше — в статті Meta Engineering про Paparazzi.
Головне
Golden Test — це автоматизована перевірка зовнішнього вигляду компонента шляхом піксельного порівняння з еталоном. Процес: (1) розробник або тестувальник створює перший знімок компонента — це «golden» (еталон). (2) Файл golden зберігається в репозиторії поряд з тестом. (3) При подальших запусках тест знову рендерить компонент та порівнює його зі збереженим golden. (4) Якщо зображення збігаються — тест зелений. Якщо різняться — тест червоний з дифом. Рішення: або зміни очікувані (оновлюємо golden), або це баг.
How the golden як генерується — бібліотека рендерить компонент в офекранний буфер (Android: Canvas, iOS: UIGraphicsImageRenderer) без реального дисплея. Це означає, що golden-тести працюють на CI без емулятора екрана (virtual display), що прискорює виконання. Paparazzi на Android використовує Layoutlib з Android Studio — той самий рушій, що й Layout Editor. iOSSnapshotTestCase використовує UIKit-рендеринг в CGImage. Результат — PNG-файл фіксованого розміру.
Golden-файли — PNG-знімок одного екрану (1080x1920) займає 200–800 КБ залежно від складності. Для проєкту з 500 golden-тестами це ~100–400 МБ в репозиторії. Рішення: (1) зберігати golden в Git LFS. (2) Використовувати стиснення PNG (pngcrush, oxipng). (3) Зберігати golden на окремому сховищі (S3) і підтягувати при збірці. В IT Sectr ми зберігаємо golden в Git LFS з порогом в 1 МБ на файл — цього достатньо для 90% тестів.
Flaky golden тести — головна проблема golden-тестів. Різні GPU, версії шрифтів та антиаліасинг дають мікро-відмінності в пікселях. Рішення: threshold (допустимий відсоток різних пікселів), fuzzy comparison (розмите порівняння) та запуск на однакових CI-агентах (однаковий GPU, ОС, версія емулятора). В Paparazzi використовується pixel-perfect порівняння, тому CI-агенти повинні бути ідентичними.
Golden Test — це вид screenshot-тестування з фіксованим еталоном. Термін «golden» означає, що еталон затверджений (прийнятий) командою та зберігається в репозиторії. Будь-яка зміна зображення вимагає усвідомленого рішення розробника: оновити golden або виправити код. Golden Test працює на рівні окремих компонентів (Composable, UIView) та не потребує реального пристрою.
Screenshot Test — більш широке поняття. Screenshot-тест може захоплювати цілий екран з реальними даними, навігацією, системним статус-баром та анімаціями. Screenshot-тести часто запускаються на реальних пристроях або емуляторах через UI Automator (Android) або XCUITest (iOS). Golden-тести працюють в unit-test оточенні (JVM, XCTest) без емулятора та захоплюють лише окремий компонент.
| Характеристика | Golden Test | Screenshot Test |
|---|---|---|
| Рівень | Компонент/Composable/View | Цілий екран |
| Оточення | Unit-test (off-screen buffer) | Device/Emulator |
| Швидкість | 50–200 мс на тест | 2–30 секунд на тест |
| Анімації | Не підтримуються | Підтримуються (з паузами) |
| CI без GPU | Працює (Layoutlib) | Вимагає емулятора |
| Складність налаштування | Низька | Висока (Emulator/Device Farm) |
| Flakiness | Середня (різні GPU) | Висока (емулятор, час) |
Golden vs Screenshot — golden-тести для перевірки окремих UI-компонентів (кнопка, картка, діалог) при кожному коміті. Screenshot-тести — для E2E-перевірки цілих екранів перед релізом. Golden-тести дають швидкий зворотний зв’язок розробнику, screenshot-тести — впевненість у цілісності всього додатка. В IT Sectr ми використовуємо golden-тести для Pull Request (3–5 хвилин), а screenshot-тести — nightly (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"
}
// Golden test для Compose компонента
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 — альтернатива Paparazzi з підтримкою Compose, View та порівняння зображень. Відмінність: Roborazzi працює через Robolectric та підтримує threshold (відсоток допустимого розходження пікселів). Це знижує flakiness при різних GPU на CI. Roborazzi також уміє створювати GIF-анімацію змін (до/після/диф), що зручно для код-рев’ю. Формат golden-файлів: PNG + JSON-метадані.
Оновлення golden — після intentional зміни UI розробник видаляє старі golden-файли та запускає тести з флагом record. Paparazzi заново створює всі golden-файли. Потім розробник комітить нові golden разом зі зміною коду. В Code Review ревьювер бачить diff старих та нових golden. Якщо зміни схвалені — PR зливається. Якщо ні — розробник править код та перезапускає тести. Ніколи не оновлюйте golden автоматично на CI — лише локально.
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-файли — PNG в папці ReferenceImages. Перевага: працює з UIKit без SwiftUI та підтримує iOS 12+. Недолік: не оновлює golden автоматично — потрібно запускати з флагом record. SwiftSnapshotTesting більш сучасний та рекомендований для нових проєктів.
Device-specific golden — golden-файли різняться для різних розмірів екранів та орієнтацій. Стандартний підхід: іменувати golden як TestName@3x~iPhone14.png. SwiftSnapshotTesting автоматично додає суфікс пристрою, якщо вказано параметр .image(on: .iPhoneSe). На Android Paparazzi використовує DeviceConfig для задання розміру. Храніть golden для кожного підтримуваного device form factor окремо. Не використовуйте один golden для різних розмірів — це призведе до flaky тестів.
CI pipeline — golden-тести повинні запускатися на кожному Pull Request. Якщо тест падає, CI показує diff-зображення як артефакт збірки. Розробник переглядає diff та приймає рішення. Важливо: golden-файли, згенеровані на CI, ніколи не комітяться автоматично. Тільки локальна генерація розробником після intentional зміни. GitHub Actions та GitLab CI підтримують завантаження артефактів (png, html) для перегляду diff в браузері.
Розмір репозиторію — golden-файли швидко ростуть. 500 тестів = 100–400 МБ PNG. Рішення: (1) Git LFS — кожний golden зберігається в LFS, клонується лише при checkout. (2) Храніти golden в окремому репозиторії та підключати як субмодуль. (3) S3 + кешування — golden на S3, CI завантажує лише змінені файли за чексумою. В IT Sectr використовуємо Git LFS з track *.png filter=lfs diff=lfs merge=lfs text=false. Локально golden лежать в src/test/goldens/.
Code Review golden — звичайний git diff не показує змін PNG. Рішення: (1) GitHub відкриває PNG-зображення при кліці. (2) Використовувати Review Apps, де диф golden видний в браузері. (3) Генерувати HTML-звіт з колонками до/після/диф. Paparazzi створює HTML-звіт з трьома колонками: actual, expected, diff. Звіт прикріпляється до CI-артефактів. Reviewers переглядають звіт, не завантажуючи файли локально.
Коли оновлювати golden — лише після усвідомленої зміни UI. Зміна шрифту, кольору, відступу, іконки — golden повинен оновитися. Додавання нової кнопки, перестановка елементів — golden повинен оновитися. Баг-фікс, який змінює зовнішній вигляд — golden повинен оновитися. Рефакторинг без зміни UI — golden не повинен оновлюватися. Якщо golden змінюється без зміни UI-коду — це flaky test через оточення, шукайте причину в CI-агентах або версіях залежностей.
Часто задавані питання
Golden Test — snapshot-тест на рівні компонента в unit-test оточенні (швидкий, без емулятора). Screenshot Test — захоплення повного екрану на пристрої або емуляторі (повільний, але реалістичний). Golden працює з off-screen buffer, screenshot — з реальним дисплеєм. Golden підходить для CI при кожному коміті, screenshot — для nightly-прогонів перед релізом.
Основні причини: (1) Різні GPU на CI — використовуйте однакові CI-агенти. (2) Різні версії шрифтів — фіксуйте версію ОС. (3) Різний anti-aliasing — налаштуйте threshold (Roborazzi, iOSSnapshotTestCase). (4) Анімації — відключайте анімації в тестах. (5) Системні елементи (статус-бар) — використовуйте безрамковий device config. Paparazzi не піддає flakiness через Layoutlib.
Так. Paparazzi має вбудовану підтримку Compose через paparazzi.snapshot { }. Roborazzi також підтримує Compose. На iOS SwiftSnapshotTesting працює з SwiftUI через UIHostingController. Compose-компоненти рендеряться через Layoutlib, SwiftUI — через UIKit-рендеринг. Обмеження: не підтримуються анімації Compose та SwiftUI — golden-тест захоплює лише початковий стан.
Ніколи не автоматизуйте прийняття golden на CI. Тільки локально: розробник видаляє старі golden-файли з директорії та запускає тести з флагом record (Paparazzi: record=true, SwiftSnapshotTesting: record=true). Golden-файли перестворюються. Розробник переглядає кожний golden на предмет коректності, комітить зміни разом з кодом. Автоматичне прийняття на CI призведе до пропуску багів в UI.
Golden-тести швидші за інструментальні (UI Automator, XCUITest). Один golden-тест виконується за 50–200 мс (Paparazzi: 100–150 мс на середньому MacBook Pro). 500 golden-тестів = 25–100 секунд. Порівняйте з screenshot-тестами через емулятор: 5–30 секунд на тест. Golden-тести не уповільнюють збірку: 100 тестів = ~15 секунд, що прийнятно для pre-merge перевірки.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також