Golden Test (snapshot-test، تست استاندارد) — روش تست بصری 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-testها روی CI بدون شبیهساز صفحه (virtual display) کار میکنند که اجرا را سرعت میبخشد. Paparazzi روی Android از Layoutlib از Android Studio استفاده میکند — همان موتور Layout Editor. iOSSnapshotTestCase از رندر UIKit به CGImage استفاده میکند. نتیجه — فایل PNG با اندازه ثابت.
فایلهای golden — اسکرینشات PNG یک صفحه (1080x1920) بسته به پیچیدگی 200-800 کیلوبایت اشغال میکند. برای پروژهای با 500 golden-test این ~100-400 مگابایت در مخزن است. راهحلها: (1) ذخیره golden در Git LFS. (2) استفاده از فشردهسازی PNG (pngcrush، oxipng). (3) ذخیره golden در ذخیرهسازی جداگانه (S3) و دریافت هنگام ساخت. در IT Sectr golden را با آستانه 1 مگابایت به ازای هر فایل در Git LFS ذخیره میکنیم — این برای 90٪ تستها کافی است.
تستهای flaky golden — مشکل اصلی golden-testها. GPUهای مختلف، نسخههای فونت و ضدآلیاسینگ تفاوتهای میکرو در پیکسلها ایجاد میکنند. راهحلها: threshold (درصد مجاز پیکسلهای متفاوت)، fuzzy comparison (مقایسه محو) و اجرا روی عوامل CI یکسان (GPU، OS، نسخه شبیهساز یکسان). در Paparazzi از مقایسه pixel-perfect استفاده میشود، بنابراین عوامل CI باید یکسان باشند.
Golden Test — نوعی screenshot-testing با استاندارد ثابت است. اصطلاح «golden» به این معناست که استاندارد توسط تیم تأیید شده و در مخزن ذخیره میشود. هر تغییر تصویر نیاز به تصمیم آگاهانه توسعهدهنده دارد: بهروزرسانی golden یا اصلاح کد. Golden Test در سطح کامپوننتهای جداگانه (Composable، UIView) کار میکند و به دستگاه واقعی نیاز ندارد.
Screenshot Test — مفهوم گستردهتری است. Screenshot-test میتواند کل صفحه را با دادههای واقعی، ناوبری، نوار وضعیت سیستم و انیمیشنها ضبط کند. Screenshot-testها اغلب روی دستگاههای واقعی یا شبیهسازها از طریق UI Automator (Android) یا XCUITest (iOS) اجرا میشوند. Golden-testها در محیط unit-test (JVM، XCTest) بدون شبیهساز کار میکنند و فقط یک کامپوننت را ضبط میکنند.
| ویژگی | Golden Test | Screenshot Test |
|---|---|---|
| سطح | کامپوننت/Composable/View | کل صفحه |
| محیط | Unit-test (بافر خارج از صفحه) | دستگاه/شبیهساز |
| سرعت | 50-200 میلیثانیه به ازای هر تست | 2-30 ثانیه به ازای هر تست |
| انیمیشنها | پشتیبانی نمیشوند | پشتیبانی میشوند (با مکث) |
| CI بدون GPU | کار میکند (Layoutlib) | نیاز به شبیهساز دارد |
| پیچیدگی راهاندازی | کم | زیاد (شبیهساز/Device Farm) |
| Flakiness | متوسط (GPUهای مختلف) | زیاد (شبیهساز، زمان) |
Golden در مقابل Screenshot — golden-testها برای بررسی کامپوننتهای UI جداگانه (دکمه، کارت، دیالوگ) در هر commit. Screenshot-testها — برای بررسی E2E کل صفحات قبل از انتشار. Golden-testها بازخورد سریع به توسعهدهنده میدهند، screenshot-testها — اطمینان از یکپارچگی کل برنامه. در IT Sectr از golden-testها برای Pull Request (3-5 دقیقه) و از screenshot-testها برای 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 را دوباره ایجاد میکند. سپس توسعهدهنده golden جدید را همراه با تغییر کد commit میکند. در بازبینی کد، بازبین 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 مدرنتر است و برای پروژههای جدید توصیه میشود.
golden مخصوص دستگاه — فایلهای golden برای اندازههای مختلف صفحه و جهتها متفاوت هستند. رویکرد استاندارد: نامگذاری golden به صورت TestName@3x~iPhone14.png. SwiftSnapshotTesting به طور خودکار پسوند دستگاه را اضافه میکند اگر پارامتر .image(on: .iPhoneSe) داده شود. روی Android، Paparazzi از DeviceConfig برای تعیین اندازه استفاده میکند. golden را برای هر فرم فاکتور دستگاه پشتیبانی شده جداگانه ذخیره کنید. از یک golden برای اندازههای مختلف استفاده نکنید — این منجر به تستهای flaky میشود.
CI pipeline — golden-testها باید در هر Pull Request اجرا شوند. اگر تست شکست بخورد، CI تصویر diff را به عنوان مصنوع ساخت نشان میدهد. توسعهدهنده diff را بررسی میکند و تصمیم میگیرد. مهم: فایلهای golden تولید شده روی CI هرگز به صورت خودکار commit نمیشوند. فقط تولید محلی توسط توسعهدهنده پس از تغییر عمدی. 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/ قرار دارند.
بازبینی کد 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 ناشی از محیط است، علت را در عوامل CI یا نسخههای وابستگی جستجو کنید.
سوالات متداول
Golden Test — snapshot-test در سطح کامپوننت در محیط unit-test (سریع، بدون شبیهساز). Screenshot Test — ضبط کل صفحه روی دستگاه یا شبیهساز (کند، اما واقعی). Golden با بافر خارج از صفحه کار میکند، screenshot — با نمایشگر واقعی. Golden برای CI در هر commit مناسب است، screenshot — برای اجراهای شبانه قبل از انتشار.
علل اصلی: (1) GPUهای مختلف روی CI — از عوامل CI یکسان استفاده کنید. (2) نسخههای مختلف فونت — نسخه OS را ثابت کنید. (3) ضدآلیاسینگ مختلف — threshold را تنظیم کنید (Roborazzi، iOSSnapshotTestCase). (4) انیمیشنها — انیمیشنها را در تستها غیرفعال کنید. (5) عناصر سیستمی (نوار وضعیت) — از device config بدون حاشیه استفاده کنید. Paparazzi به دلیل Layoutlib در معرض flakiness نیست.
بله. Paparazzi پشتیبانی داخلی از Compose از طریق paparazzi.snapshot { } دارد. Roborazzi نیز از Compose پشتیبانی میکند. در iOS، SwiftSnapshotTesting از طریق UIHostingController با SwiftUI کار میکند. کامپوننتهای Compose از طریق Layoutlib رندر میشوند، SwiftUI — از طریق رندر UIKit. محدودیت: انیمیشنهای Compose و SwiftUI پشتیبانی نمیشوند — golden-test فقط حالت اولیه را ضبط میکند.
هرگز پذیرش golden را روی CI خودکار نکنید. فقط به صورت محلی: توسعهدهنده فایلهای golden قدیمی را از دایرکتوری حذف میکند و تستها را با پرچم record اجرا میکند (Paparazzi: record=true، SwiftSnapshotTesting: record=true). فایلهای golden دوباره ایجاد میشوند. توسعهدهنده هر golden را از نظر صحت بررسی میکند، تغییرات را همراه با کد commit میکند. پذیرش خودکار روی CI منجر به نادیده گرفتن باگهای UI میشود.
Golden-testها سریعتر از تستهای ابزاری (UI Automator، XCUITest) هستند. یک golden-test در 50-200 میلیثانیه اجرا میشود (Paparazzi: 100-150 میلیثانیه روی MacBook Pro متوسط). 500 golden-test = 25-100 ثانیه. با screenshot-testها از طریق شبیهساز مقایسه کنید: 5-30 ثانیه به ازای هر تست. Golden-testها ساخت را کند نمیکنند: 100 تست = ~15 ثانیه، که برای بررسی pre-merge قابل قبول است.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید