• 29.08.2026 09:12:32
  • Admin Admin

Jetpack Compose ekranlarını Paparazzi ile cihaz emülatörü açmadan piksel düzeyinde doğrulayın. Kararlı fixture, tema, locale ve CI doğrulamasıyla görünmeyen UI kırılmalarını pull request aşamasında yakalayın.

Jetpack Compose'da Görsel Regresyon Testleriyle Kararlı UI

Jetpack Compose için görsel regresyon testinin sınırı

Bir jetpack compose ekranında semantics testi, butonun bulunduğunu ve tıklanabildiğini kanıtlar; ancak yanlış padding, kesilmiş başlık, koyu tema kontrastı veya tasarım sistemi token'ının yanlış uygulanmasını yakalayamaz. Paparazzi, composable ağacını Android'in layoutlib ortamında render eder ve oluşan bitmap'i repodaki referans görüntüyle karşılaştırır. Bu yaklaşımı özellikle tasarım sistemi bileşenleri, checkout özeti ve Android MVVM mimarisi içindeki UiState'ten üretilen kritik ekranlar için kullanın.

Bu testleri cihaz davranışıyla karıştırmayın. Paparazzi JVM üzerinde çalıştığı için gerçek GPU compositor'ını, IME animasyonlarını, WebView'i, kamera preview'ını ve bazı RenderEffect sonuçlarını cihaz kadar doğru temsil etmez. Örneğin blur kullanan bir composable için aynı senaryoyu bağlı cihazda Compose UI testiyle ayrıca çalıştırın:

./gradlew :app:connectedDebugAndroidTest
./gradlew :app:verifyPaparazziDebug
İlk komut davranışsal cihaz testlerini, ikinci komut ise piksel farkını hedefler; ikisi aynı hata sınıfını ölçmez.

Android eğitimi projelerinde Paparazzi kurulumu ve ilk snapshot

Bir android eğitimi veya android eğitim laboratuvarında kurulumu sürüm numarasını build.gradle.kts dosyalarına dağıtmak yerine version catalog ile tek noktada tutun. Aşağıdaki yapı, eklenti ve test bağımlılığını aynı katalog girdisinden çözer; Gradle yükseltmesinde hangi sürüm kombinasyonunun değiştiği review sırasında görünür olur.

// gradle/libs.versions.toml
[versions]
paparazzi = "PINNED_VERSION"

[plugins]
paparazzi = { id = "app.cash.paparazzi", version.ref = "paparazzi" }

[libraries]
paparazzi = { module = "app.cash.paparazzi:paparazzi", version.ref = "paparazzi" }

// app/build.gradle.kts
plugins {
    alias(libs.plugins.paparazzi)
}

dependencies {
    testImplementation(libs.paparazzi)
    testImplementation(libs.junit4)
}
PINNED_VERSION yerine ekibinizin Gradle, AGP ve JDK matrisiyle doğruladığı sürümü koyun. Bu eklentinin JVM testi çalıştırdığı unutulursa, yalnızca androidTest'e bağımlılık eklemek yaygın ve sessiz bir kurulum hatasıdır.

İlk snapshot'ta ekranı ağ, saat ve rastgele veri olmadan render edin. `Instant.now()`, UUID veya repository'den gecikmeli gelen Flow, referans bitmap'in her çalışmada değişmesine neden olur. Kotlin eğitimi kapsamında fixture üretirken immutable veri kullanmak bu nedenle test edilebilirlik açısından somut bir kazançtır:

class ProductCardSnapshotTest {
    @get:Rule
    val paparazzi = Paparazzi(
        deviceConfig = DeviceConfig.PIXEL_5,
        theme = "android:Theme.Material.Light.NoActionBar"
    )

    @Test
    fun productCard_discountedStock() {
        val item = ProductUiModel(
            id = "sku-42",
            title = "Mekanik Klavye",
            price = "1.299 TL",
            oldPrice = "1.599 TL",
            imageUrl = null,
            stockLabel = "Son 3 ürün"
        )

        paparazzi.snapshot {
            AppTheme(darkTheme = false) {
                Surface { ProductCard(item = item) }
            }
        }
    }
}
Görsel varlığı Coil veya Glide ile URL'den yüklemek yerine `imageUrl = null` için sabit placeholder render edin ya da sahte bir `ImageLoader` enjekte edin. Aksi halde testin sonucu CDN yanıtına ve decoder davranışına bağlanır.

Kotlin kursu için deterministik UI durum matrisi

Tek bir happy-path ekranı yeterli değildir. Bir kotlin kursu örneğinde `Loading`, `Content`, `Empty` ve uzun metinli `Error` durumlarını ayrı snapshot olarak üretin. Özellikle `Error` metnini kısa bir İngilizce cümleyle sınırlamak, Türkçe veya Almanca yerelleştirmede satır taşması hatasını gizler. Test adı görüntünün iş kuralını açıklamalıdır; `screen_1` gibi adlar fark oluştuğunda code review kararını yavaşlatır.

sealed interface OrdersUiState {
    data object Loading : OrdersUiState
    data object Empty : OrdersUiState
    data class Error(val message: String) : OrdersUiState
    data class Content(val orders: List<OrderRowUi>) : OrdersUiState
}

@Test
fun orders_error_with_long_message() = paparazzi.snapshot {
    AppTheme {
        OrdersScreen(
            state = OrdersUiState.Error(
                "Siparişler alınamadı. Bağlantınızı kontrol edip tekrar deneyin."
            ),
            onRetry = {}
        )
    }
}
Bu düzen, android mvvm mimarisi içinde ViewModel'i snapshot testine sokmadan reducer çıkışını doğrudan doğrular. ViewModel, dispatcher ve repository mock'larını burada kullanmak hem test süresini uzatır hem de görüntü farkının kaynağını belirsizleştirir.

Font scale ve locale'ı ayrıca hedefleyin. `sp` yerine yanlışlıkla `dp` ile metin boyutu veren bir bileşen, varsayılan font scale'da düzgün görünür ama erişilebilirlik ayarında taşar. Paparazzi test setine en az `Locale("tr", "TR")`, RTL için `Locale("ar")` ve büyük font ölçeğiyle çalışan bir cihaz konfigürasyonu ekleyin. Konfigürasyon değiştirirken sadece cihaz genişliğini değil `fontScale`, `locale` ve `layoutDirection` değerlerini birlikte sabitleyin; aksi halde geliştiricinin makinesindeki varsayılan locale referans üretimine sızabilir. Bu ayrıntı, android studio eğitimi sırasında ekran görüntüsünü sadece preview'de kontrol etmenin neden yeterli olmadığını da gösterir.

Referans görüntüleri CI'da üretme, inceleme ve doğrulama

Referans görüntüyü yalnızca tasarım değişikliğini yapan geliştirici güncellesin. Önce yerelde görüntüyü kaydedin, sonra doğrulayın:

./gradlew :app:recordPaparazziDebug
./gradlew :app:verifyPaparazziDebug
git status -- src/test/snapshots
Paparazzi'nin ürettiği snapshot dizinini Git'e dahil edin ve pull request'te PNG diff'ini zorunlu inceleme çıktısı yapın. CI'da sadece `verifyPaparazziDebug` çalışmalıdır; `record` komutunu CI'a koymak, beklenmeyen kırılmayı yeni doğru çıktı gibi kaydederek testin korumasını ortadan kaldırır.

Bir fark oluştuğunda doğrudan baseline'ı kabul etmeyin. Önce test çıktısındaki expected, actual ve delta görüntülerini açın; değişim yalnızca bir metnin 1 piksel kaymasıysa Compose compiler, font veya layoutlib değişimini dependency lock dosyasıyla ilişkilendirin. Değişim tüm yüzeyin rengindeyse tema token'ı veya dynamic color kaynağına bakın. CI işini JDK ve Gradle wrapper sürümü sabit bir container imajında çalıştırmak önemlidir: font rasterization ve layoutlib girdileri değiştiğinde aynı kaynak kodundan farklı bitmap çıkabilir. Bu kontrol, mobil uygulama geliştirme eğitimi projelerinde 'benim makinemde geçti' türü görsel farkları yeniden üretilebilir hale getirir.

Play Store yayınlama öncesi görsel test paketi seçimi

Play Store yayınlama öncesinde her route'u bitmap ile test etmeye çalışmak yerine risk tabanlı bir paket oluşturun: fiyat ve para birimi biçimlendiren kartlar, izin reddi ekranları, boş durumlar, hata banner'ları, koyu tema ve tablet genişliği ilk adaylardır. Örneğin ödeme özeti için `tr-TR`, uzun fiyat ve indirim etiketi; profil için büyük font scale; sağdan sola destekleyen uygulama için RTL snapshot ekleyin. Bu listeyi release checklist'e `:app:verifyPaparazziDebug` zorunlu adımı olarak koymak, manuel QA'nın bulduğu her padding regresyonunu otomatik geri dönüş testine çevirir.

Bir android kursu müfredatında Paparazzi'yi tek başına kalite ölçütü olarak sunmayın. Compose UI testleriyle kullanıcı akışını, Macrobenchmark ile gerçek cihazdaki frame süresi ve startup'ı, Paparazzi ile de statik görünümü ayırın. Örneğin tıklanabilirlik için `composeTestRule.onNodeWithText("Tekrar dene").performClick()` kullanın; görsel hizalama için aynı durumun snapshot'ını alın. Bu ayrım, test başarısız olduğunda ekibin önce UI kontratı mı, event akışı mı, yoksa cihaz render'ı mı bozulduğunu kısa sürede sınıflandırmasını sağlar.

Sık Sorulan Sorular

Jetpack Compose görsel regresyon testinde Paparazzi mi emülatör mü kullanılmalı?

Statik composable görünümü için Paparazzi'yi JVM testinde kullanın; `./gradlew :app:verifyPaparazziDebug` emülatör boot süresini beklemeden bitmap farkını verir. Kamera, WebView, GPU blur, IME ve gerçek sistem bar davranışları için ise bağlı cihaz veya emülatörde `connectedDebugAndroidTest` çalıştırın.

Android MVVM mimarisi ekranını snapshot testine nasıl hazırlamalıyım?

ViewModel'i snapshot testine dahil etmeyin. ViewModel'in ürettiği `UiState` için sabit fixture kurup `Screen(state = ..., onEvent = {})` çağrısı yapın. Repository, Flow zamanlaması ve dispatcher bu testte gereksiz değişken üretir; bunları ViewModel birim testinde ayrı doğrulayın.

Kotlin eğitimi projelerinde snapshot testleri neden bazen sürekli değişiyor?

En sık nedenler `Instant.now()`, rastgele UUID, uzaktan gelen görsel, makine locale'ı ve farklı font rasterization girdileridir. Test verisini sabitleyin, locale ve cihaz konfigürasyonunu açıkça verin, ağ görsellerini fake loader ile değiştirin ve CI'da sabit JDK içeren bir container kullanın.

Android Studio eğitimi sırasında snapshot PNG dosyaları repoya eklenmeli mi?

Evet. Referans PNG dosyası testin beklenen çıktısıdır ve kaynak koduyla birlikte version control altında olmalıdır. Pull request'te PNG değişimini inceleyin; CI sadece `verifyPaparazziDebug` çalıştırmalı, baseline üreten `recordPaparazziDebug` komutu otomatik çalışmamalıdır.

AI / LLM Discovery

Bu makale Opendart Akademi Android eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt