A snapshot tesztelés a felhasználói felület automatizált ellenőrzésének módszere, melynek során a képernyő aktuális állapotát összehasonlítják a teszt előző futtatásakor elmentett referenciaképpel (snapshot). Bármilyen vizuális eltérés változásként kerül rögzítésre, ami a fejlesztő megerősítését igényli. Az elemek meglétét ellenőrző UI tesztekkel ellentétben a snapshot tesztek pixeles változásokat rögzítenek — elmozdulásokat, színeltéréseket és elrendezési hibákat. A Android Developers, 2024 szerint a snapshot tesztelés a vizuális regressziók akár 30%-t is képes felismerni, amelyeket a hagyományos UI tesztek nem észlelnek, ami nélkülözhetetlen eszközzé teszi az egységes felület fenntartásában.
Főbb pontok
Snapshot tesztelés (snapshot testing) egy technika, melynek során a teszt renderel egy felületi komponenst, elmenti a kapott képet referenciaként, és a későbbi futtatások során összehasonlítja az aktuális rendert ezzel a referenciával. Ha a képek egyeznek — a teszt sikeres. Ha eltéréseket találnak — a teszt megbukik, és a fejlesztő kap egy diff képet a megváltozott pixelek kiemelésével. A technika a webes fejlesztésből (Jest snapshots) származik és mobil platformokra lett adaptálva.
A snapshot tesztek fő értéke a váratlan vizuális változások automatikus észle. A fejlesztő megváltoztathatja a színsémát egy globális témában és véletlenszerűen tucatnyi képernyőre hathat. Az UI tesztek, amelyek a gombok és szövegek meglétét ellenőrzik, ezt nem veszik észre. A snapshot teszt rögzíti minden egyes pixel változását minden érintett képernyőn, teljes képet adva a változás hatásáról.
A Mobile DevOps Summit 2023 felmérése szerint azok a csapatok, amelyek a klasszikus UI tesztek mellett snapshot tesztelést is használnak, 40%-kal csökkentik a vizuális hibák számát a kiadásokban. Ez a megközelítés különösen hatékony a design rendszereket és komponens alapú megközelítést használó projektekben, ahol egy alapkomponens megváltoztatása tucatnyi képernyőt érinthet.
Az alapvető különbség az ellenőrzés tárgyában rejlik. UI tesztek a felületi elemek meglétét, állapotát és viselkedését ellenőrzik: „a gomb látható”, „a szöveg hibauzenetet tartalmaz”, „kattintásra új képernyő nyílik”. A snapshot tesztek a külalakot egészében ellenőrzik: elemek elhelyezkedése, távolságok, színek, betűtípusok, árnyékok és lekerekítések. Snapshot teszt arra a kérdésre válaszol, hogy ‚ képernyő úgy néz-e ki, ahogy elvárt?”, míg az UI teszt arra, hogy ‚ képernyő úgy működik-e, ahogy elvárt?”
A végrehajtási sebesség is különbözik. Az UI tesztek emulátoron vagy valódi eszközön futnak, teljes alkalmazásbetöltést igényelnek és 10 másodperctől egy percig tartanak forgatónyívenként. A Paparazzihoz hasonló könyvtárakon alapuló snapshot tesztek a komponenst virtuális környezetben renderelik az emulátor elindítása nélkül, ami 100–500 ezredmásodpercre csökkenti a tesztidőt. Egy teljes snapshot tesztkészlet (50–100 képernyő) 2–5 perc alatt fut le, szemben a hasonló UI tesztkészlet 30–60 percével.
A snapshot tesztek azonban nem váltják ki az UI teszteket. Az optimális stratégia a kombináció: a snapshot tesztek a vizuális regressziót fedik le (minden képernyő renderje alapállapotokban), az UI tesztek pedig a viselkedési regressziót (kattintás forgatókönyvek, bevitel érvényesítés, navigáció). Ez a kombináció 90%-os biztonságot nyújt a felület helyességében minimális CI végrehajtási idő mellett.
Androidon a fő eszközök a Paparazzi és a Shot. A Cash App Paparazzi-ja komponenseket renderel tesztkörnyezetben JVM-en emulátor nélkül, Layoutlib gravitációs elrendezést használva. A Karumi Shot-ja Instrumentation képernyőképeket készít valódi eszközön vagy emulátoron és hasonlítja őket össze a referenciákkal az AShot könyvtáron keresztül, figyelembe véve a felbontás és pixelsűrűség különbségeit.
Paparazzi nem igényli az emulátor elindítását — a renderelés JVM-en történik a Layoutlib segítségével, ami az egységtesztekkel összehasonlítható sebességet biztosít. A könyvtár támogatja mind a View rendszert, mind a Jetpack Compose-t. Compose-hoz a paparazzi.snapshot { MyComposable() } módosító használható. A referenciák a src/test/snapshots mappában tárolódnak és minden futtatáskor automatikusan összehasonlításra kerülnek. A maximális eltérési százalék a maxPercentDifference segítségével állítható be.
SnapshotTesting a Point-Free-tól nemcsak UIImage, hanem karakterláncok, JSON, Data és teljes Core Data tárolók összehasonlítását is támogatja. Ez univerzális eszközzé teszi nemcsak UI snapshotokhoz, hanem JSON válaszok szerializálásának és dekódolásának ellenőrzéséhez is. SwiftUI-hoz az assertSnapshot kiterjesztés használható a .image(on: .iPhone13) módosítóval. A record: true felvételi stratégia az első futtatáskor hozza létre a referenciákat.
React Native-hoz a népszerű megoldás a react-native-testing-library kombinálva a jest-image-snapshot-tal. A snapshot tesztelés webes megközelítése átkerül a mobil környezetbe a komponensek Node.js környezetben történő renderelésével és a virtuális DOM JSON snapshotjainak összehasonlításával. Ez a megközelítés gyorsabb, mint a natív, de kevésbé pontos — nem veszi figyelembe a betűtípusok és rendszerkomponensek platform-specifikus renderelési jellemzőit. Flutter-hez a goldens toolkit segítségével végezhető golden tesztelés.
Nézzük meg a snapshot teszteket Androidra (Paparazzi) és iOS-re (SnapshotTesting). Mindkét példa egy komponens — egy felhasználói kártya avatárral, névvel és állapottal — külalakját ellenőrzi. A teszt rendereli a komponenst tesztadatokkal és összehasonlítja az eredményt a tárolóban elmentett referenciaképpel.
A Paparazzi a @Test annotációt és a snapshot() metódust használja a render rögzítéséhez. A referenciák az src/test/snapshots mappában tárolódnak és automatikusan betöltődnek a következő futtatáskor az összehasonlításhoz.
class UserCardSnapshotTest {
@get:Rule
val paparazzi = Paparazzi(
theme = "Theme.MyApp",
maxPercentDifference = 0.1
)
@Test
fun userCard_defaultState() {
val card = UserCard(
name = "Alice Johnson",
status = "Online",
avatarUrl = "https://example.com/avatar.png"
)
paparazzi.snapshot(card)
}
@Test
fun userCard_offlineState() {
val card = UserCard(
name = "Bob Smith",
status = "Offline",
avatarUrl = null
)
paparazzi.snapshot(card, name = "user_card_offline")
}
}
SnapshotTesting a .snapshot() módosítót használja az assertSnapshot-on belül. A könyvtár automatikusan meghatározza a formátumot — UIImage-t UIView-hez, String-et szöveghez, Data-t bináris adatokhoz.
import SnapshotTesting
import XCTest
class UserCardSnapshotTests: XCTestCase {
func testUserCardDefaultState() {
let card = UserCardView(
name: "Alice Johnson",
status: "Online",
avatarURL: URL(string: "https://example.com/avatar.png")
)
let controller = UIHostingController(rootView: card)
assertSnapshot(matching: controller, as: .image(on: .iPhone13))
}
func testUserCardOfflineState() {
let card = UserCardView(
name: "Bob Smith",
status: "Offline",
avatarURL: nil
)
assertSnapshot(matching: card, as: .image(on: .iPhone13))
}
}
Egy tipikus munkafolyamat négy szakaszból áll. Első futtatás (record mode): az összes snapshot teszt felvételi módban fut — a referenciaképek létrejönnek és elmentődnek a tárolóba. Ezt a szakaszt a tesztek kezdeti beállításakor vagy a felület tudatos megváltoztatása után végzik el. A felvétel után a referenciák a kóddal együtt commitolódnak — a projekt részévé válnak.
A későbbi futtatások során a tesztek összehasonlító módban működnek: minden új render összehasonlításra kerül a referenciával. Ha eltéréseket találnak, egy diff kép generálódik: a referenciával egyező pixelek zölddel, az eltérők pirossal kerülnek kiemelésre. A fejlesztő tanulmányozza a diff-et és döntést hoz: ha a változás várt (tudatos tervezési változás), a referencia frissítése a record paranccsal történik; ha váratlan — a hibát kijavítják. A referenciák frissítése egyszeri paranccsal történik: Paparazzi esetén `./gradlew recordPaparazzi`, SnapshotTesting esetén `assertSnapshot(record: true)`.
A Spotify Engineering Blog (2022) szerint a leírt munkafolyamatot használó csapatok átlagosan 2 percet töltenek tesztelként a diff képek elemzésével. 50 snapshot tesztből álló készletnél a referenciák teljes frissítési ciklusa 15–20 percig tart, ami lényegesen gyorsabb, mint 50 képernyő vizuális változásainak kézi ellenőrzése.
A snapshot tesztek alapvető korlátozásokkal rendelkeznek. Környezet érzékenység: ugyanaz a komponens különbözően renderelődhet különböző OS verziókon, képernyősűrűségeken és betűkonfigurációkon. Az egyik gépen létrehozott referenciák eltérhetnek a CI szerveren történő renderelől. Megoldás — rögzített környezeti paraméterek használata: meghatározott Layoutlib verzió Paparazzihoz vagy pontos eszközmodell SnapshotTestinghez.
Antiminta 1: óriási snapshotok — egy snapshot teszt, amely az egész képernyőt rögzíti, minden apró komponensváltozásnál megbukik. A helyes megközelítés — az egyes komponensek (gomb, kártya, beviteli mező) elkülönített tesztelése. Minden komponens függetlenül kerül tesztelésre, ami pontos jelzést ad a változás forrásáról. Antiminta 2: diff-ek figyelmen kívül hagyása — a referenciák automatikus frissítése a diff képek elemzése nélkül nullára csökkenti a snapshot tesztek értékét. Minden diff a fejlesztő tudatos döntését igényli.
A Better Engineering Blog (2023) szerint a snapshot tesztek a legnagyobb hasznot a design rendszer komponenseinek és a kulcsfontosságú képernyők alapállapotokban történő lefedésével hozzák — üres, kitöltött, hibás és határállapotok. Az animációk és dinamikus állapotok snapshot tesztekkel történő lefedése nem hatékony a renderben lévő időbélyegek nemdeterminizmusa miatt — az ilyen forgatókönyvekhez a videófelvétel vagy a kézi QA ellenőrzés alkalmasabb.
Gyakran Ismételt Kérdések
Nem, a snapshot tesztek a külalakot, míg az UI tesztek a felület viselkedését ellenőrzik. Az optimális stratégia a két megközelítés kombinálása: snapshotok a vizuális regresszióhoz, UI tesztek a forgatókönyvek és navigáció ellenőrzéséhez. A snapshotok arra a kérdésre válaszolnak, hogy „helyesen néz ki?”, az UI tesztek pedig arra, hogy „helyesen működik?”
A referenciák minden tudatos tervezési változáskor frissülnek: új témaszín, módosított távolságok, elemek hozzáadása vagy eltávolítása. Frissítés a record módon keresztül történik, majd a diff képek code review során kerülnek ellenőrzésre, hogy a változások megfeleljenek a tervező elvárásainak.
Elsősorban a design rendszer komponenseit — gombok, kártyák, beviteli mezők, modális ablakok. Majd a kulcsfontosságú képernyőket alapállapotokban. Ne teszteljen snapshotokkal animációkat, WebView-t, térképeket és dinamikus tartalmú képernyőket — ezeknél a snapshotok a nemdeterminizmus miatt hamis hibákat okoznak.
Használja ugyanazt az API szintet a record és a test módokhoz. Paparazzi esetén állítson be egy meghatározott Layoutlib verziót a konfigurációban. SnapshotTesting esetén rögzítse az eszközmodellt. Az Android 14-en létrehozott referenciák eltérhetnek az Android 12-n történő renderelől a rendszerbetűtípusok és a Material téma változásai miatt.
CI-ban a snapshot tesztek ellenőrző módban (verify) futnak. Ha a teszt megbukik, a CI megjeleníti a diff képet a build artefaktumokban. A record mód (referenciák frissítése) lokálisan, a fejlesztő által vagy külön CI feladatban történik manuális indítással. A referenciaképeket kötelező a tárolóba commitolni.
Összegzés
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is