Golden Test (snapshot-тест, эталонное тестирование) — метод визуального тестирования UI, при котором текущий рендер компонента сравнивается с заранее сохранённым эталонным изображением (golden-файлом). Если пиксельные изменения превышают заданный порог, тест падает и генерирует diff-изображение. Разработчик просматривает diff и либо принимает изменения (обновляет golden), либо фиксит баг. Подробнее — в статье Meta Engineering о Paparazzi.
Главное
Golden Test — это автоматизированная проверка внешнего вида компонента путём попиксельного сравнения с эталоном. Процесс: (1) разработчик или тест создаёт первый снимок компонента — это «golden» (эталон). (2) Файл golden сохраняется в репозиторий рядом с тестом. (3) При последующих запусках тест рендерит компонент заново и сравнивает с сохранённым golden. (4) Если изображения совпадают — тест зелёный. Если различаются — тест красный с диффом. Решение: либо изменения ожидаемые (обновляем golden), либо это баг.
Как генерируется golden — библиотека рендерит компонент в off-screen buffer (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 на отдельном storage (S3) и подтягивать при сборке. В IT Sectr мы храним golden в Git LFS с порогом в 1 МБ на файл — этого достаточно для 90% тестов.
Flaky golden tests — главная проблема 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 reviewer видит 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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также