Golden Test (snapshot-тест, еталонно тестване) — метод за визуално тестване на UI, при който текущият рендер на компонент се сравнява с предварително запазено еталонно изображение (golden-файл). Ако пикселните промени надвишат зададен праг, тестът пропада и генерира diff-изображение. Разработчикът преглежда diff и или приема промените (актуализира golden), или поправя грешката. Повече в статията на Meta Engineering за Paparazzi.
Основни точки
Golden Test — е автоматизирана проверка на външния вид на компонент чрез сравнение пиксел по пиксел с еталон. Процес: (1) разработчикът или тестерът прави първия снимка на компонента — това е „golden" (еталонът). (2) Golden-файлът се запазва в хранилището до теста. (3) При следващи изпълнения тестът рендерира отново компонента и го сравнява със запазения golden. (4) Ако изображенията съвпадат — тестът е зелен. Ако се различават — тестът е червен с diff. Решение: или промените са очаквани (актуализираме golden), или е грешка.
Как се генерира 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 KB в зависимост от сложността. За проект с 500 golden-теста това е ~100-400 MB в хранилището. Решения: (1) съхраняване на golden в Git LFS. (2) Използване на PNG компресия (pngcrush, oxipng). (3) Съхраняване на golden в отделно хранилище (S3) и изтегляне при изграждане. В IT Sectr съхраняваме golden в Git LFS с праг от 1 MB на файл — това е достатъчно за 90% от тестовете.
Flaky golden тестове — основният проблем на golden-тестовете. Различни GPU, версии на шрифтове и антиалиасинг дават микро-разлики в пикселите. Решения: threshold (допустим процент различни пиксели), fuzzy comparison (размито сравнение) и изпълнение на идентични CI агенти (същият GPU, OS, версия на емулатор). В 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 (извънекранен буфер) | Устройство/Емулатор |
| Скорост | 50-200 ms на тест | 2-30 секунди на тест |
| Анимации | Не се поддържат | Поддържат се (с паузи) |
| CI без GPU | Работи (Layoutlib) | Изисква емулатор |
| Сложност на настройката | Ниска | Висока (Емулатор/Device Farm) |
| Flakiness | Средна (различни GPU) | Висока (емулатор, време) |
Golden vs Screenshot — golden-тестове за проверка на отделни UI компоненти (бутон, карта, диалог) при всеки commit. 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 анимации на промените (преди/след/diff), което е удобно за преглед на код. Формат на golden-файловете: PNG + JSON метаданни.
Актуализиране на golden — след преднамерена промяна на UI, разработчикът изтрива старите golden-файлове и пуска тестовете с флаг record. Paparazzi създава отново всички golden-файлове. След това разработчикът commit-ва новия golden заедно с промяната на кода. При преглед на код, рецензентът вижда 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 за всеки поддържан форм фактор на устройство поотделно. Не използвайте един golden за различни размери — това ще доведе до flaky тестове.
CI pipeline — golden-тестовете трябва да се изпълняват при всеки Pull Request. Ако тестът пропадне, CI показва diff-изображението като артефакт на изграждане. Разработчикът преглежда diff и взема решение. Важно: golden-файловете, генерирани на CI, никога не се commit-ват автоматично. Само локално генериране от разработчика след преднамерена промяна. GitHub Actions и GitLab CI поддържат качване на артефакти (png, html) за преглед на diff в браузъра.
Размер на хранилището — golden-файловете растат бързо. 500 теста = 100-400 MB 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/.
Преглед на код golden — обикновеният git diff не показва промени в PNG. Решения: (1) GitHub отваря PNG изображения при клик. (2) Използване на Review Apps, където golden-diff е видим в браузъра. (3) Генериране на HTML отчет с колони преди/след/diff. Paparazzi създава HTML отчет с три колони: actual, expected, diff. Отчетът се прикачва към CI артефактите. Рецензентите преглеждат отчета без да изтеглят файлове локално.
Кога да актуализирате golden — само след съзнателна промяна на UI. Промяна на шрифт, цвят, разстояние, икона — golden трябва да се актуализира. Добавяне на нов бутон, пренареждане на елементи — golden трябва да се актуализира. Поправка на грешка, която променя външния вид — golden трябва да се актуализира. Рефакториране без промяна на UI — golden не трябва да се актуализира. Ако golden се променя без промяна на UI кода — това е flaky test, причинен от средата, търсете причината в CI агентите или версиите на зависимостите.
Често задавани въпроси
Golden Test — snapshot-тест на ниво компонент в unit-test среда (бърз, без емулатор). Screenshot Test — заснемане на целия екран на устройство или емулатор (бавен, но реалистичен). Golden работи с извънекранен буфер, screenshot — с реален дисплей. Golden е подходящ за CI при всеки commit, screenshot — за nightly преди пускане.
Основни причини: (1) Различни GPU на CI — използвайте идентични CI агенти. (2) Различни версии на шрифтове — фиксирайте версията на ОС. (3) Различен антиалиасинг — настройте 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 за коректност, commit-ва промените заедно с кода. Автоматичното приемане на CI ще доведе до пропускане на грешки в UI.
Golden-тестовете са по-бързи от инструменталните тестове (UI Automator, XCUITest). Един golden-тест се изпълнява за 50-200 ms (Paparazzi: 100-150 ms на среден MacBook Pro). 500 golden-теста = 25-100 секунди. Сравнете със screenshot-тестовете чрез емулатор: 5-30 секунди на тест. Golden-тестовете не забавят изграждането: 100 теста = ~15 секунди, което е приемливо за pre-merge проверка.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също