Robolectric ve Roborazzi İle Android Testleri

İbrahim Ethem Şen
Kotlin Türkiye
Published in
4 min readFeb 23, 2024

Mobil uygulama pazarı genişledikçe yeni uygulamalar ve özellikler eklenme süreçleri de hızlandı. Uygulamaları güncellemek istediğimizde şirket özelinde belirlenecek bazı kontrollerle bu süreci ilerletiriz. Kontrol listemiz uygulamanın işlevselliği,yeni özelliklerin çalıştığının teyidi vb. olabilir.

Kontrolleri yapan kişiler uygulamanın belirlenen görünüm ve işlevsellikte çalışıp, çalışmadığı ile ilgilenir. Uygulama kodunuzun nasıl yazıldığı ile ilgilenmezler.

Bu süreci çeşitli testler ile otomatikleştirebiliriz. Süreci otomatikleştirmek geliştirmenin hızlanmasına sebep olur.

Robolectric Testleri

Robolectric bir cihaza ihtiyaç duymadan JVM üzerinde Android Framework kullanarak testler yapmamızı sağlar. Robolectric 4.10 versiyonundan itibaren native grafiklere destek sağlar. Projemize ekleyerek canvas ve komponent olarak iki örnek üzerinden bakalım.

build.gradle(app) ekleyelim.

android {
testOptions {
unitTests {
includeAndroidResources = true
}
}
}

dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.11.1'
}

Canvas

Proje içerisinde unit test klasörünün altında bir RobolectricTest class’ı oluşturalım

Robolectric’i ve native grafikleri kullanmak için annotation’ları ekliyoruz.

@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(sdk = [33])
class RobolectricTest {}

Native grafik özelliğini test etmek için bir görünüm çizelim ve bunu kayıt ederek doğruluğunu kontrol edelim. Bunun için canvas da kırmızı arka plana sahip içerisinde beyaz bir daire çizelim ve png çıktısını alalım.


@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(sdk = [33])
class RobolectricTest {
@Test
fun `draw a circle `() {
val bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888)
val paint = Paint().apply {
color = Color.WHITE
}

Canvas(bitmap).apply {
drawColor(Color.RED)
drawCircle(100f, 100f, 75f, paint)
}

bitmap.compress(Bitmap.CompressFormat.PNG, 100, FileOutputStream("robolectric.png"))
}
}

Testi çalıştırdığınızda proje içerisinde isimlendirdiğimiz şekilde bir robolectric.png dosyası oluşmalıdır.

Android View Komponent

Komponentlerin oluşturulması için activity ve context’e ihtiyacımız var. Bunun için bir activity oluşturup lifecycle adımlarını simüle etmemiz gerekli.

@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(sdk = [33])
class ViewTest {
private lateinit var view: Button

@Before
fun setUp() {
val activityController = Robolectric.buildActivity(Activity::class.java)
val activity = activityController.create().get()

view = Button(activity)

activity.setContentView(view)
activityController.start().resume().visible()
}
}

Artık tek yapmamız gereken komponenti çizmek ve bir önceki örnekte olduğu gibi çıktısını almak.

    @Test
fun `draw a button`() {
view.apply {
text = "Kotlin Mükemmeldir !"
drawToBitmap().compress(Bitmap.CompressFormat.PNG, 100, FileOutputStream("button.png"))
}
}
button.png

Roborazzi

Robolectric ile entegre olarak testleri geliştirir native grafikler ile ekran görüntülerini yakalar ve detaylı geribildirim sağlar.

Entegrasyon

Plugins ->

id("io.github.takahirom.roborazzi") version "1.8.0-alpha-2" apply false
id("io.github.takahirom.roborazzi")

Dependencies ->

testImplementation("io.github.takahirom.roborazzi:roborazzi:1.8.0-alpha-2")
testImplementation("io.github.takahirom.roborazzi:roborazzi-compose:1.8.0-alpha-2")

Roborazzi,Compose-Espresso ve Hilt ile uyumlu çalışabilir. ComposeRule için projeye ekstra junit4 ekliyoruz.

testImplementation("androidx.compose.ui:ui-test-junit4-android")

Komponent hazırlamakla uğraşmamak için burada ki komponenti kullanacağım. Surface içerisine ekleyelim.


Box(modifier = Modifier.height(120.dp)){
DiscountVoucherCard()
}

ActivityTest class’ı oluşturalım. Roborazzi’yi entegre ettiğimiz captureRoboImage kullanarak görüntüleri kolayca kaydedebiliriz.

@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(sdk = [33], qualifiers = RobolectricDeviceQualifiers.Pixel6)
class ActivityTest {

@get:Rule
val activityRule = createAndroidComposeRule<MainActivity>()

@Test
fun `screenshot activity`() {
activityRule.onRoot().captureRoboImage("activity.png")
}

}

Testi çalıştırdığınızda şu an herhangi bir şey olmaz. Projede gradle.properties record’u ekleyerek test edebilirsiniz.

roborazzi.test.record=true

Properties’e ekleme yapmak yerine gradle üzerinden de çalıştırabilirsiniz.

./gradlew recordRoborazziDebug

Test’i çalıştırdığınızda cihazda activity’nin görünümünün aynısını ekran görüntüsü olarak elde edersiniz ve bu esnada herhangi bir cihaz çalışmaz. Tamamen JVM üzerinde bu işlem yapılır.

Record dediğimizde referans bir görsel oluşturulur.Referans görsel uygulamamızdan beklediğimiz görünümün çıktısıdır.

Verify komutunu kullandığımızda yeni alınan görüntü referans görüntü ile piksel boyutunda karşılaştırılır.

Test başarılı ise görünümde herhangi bir değişiklik olmamış demektir. Başarısız olursa bize görseller arasındaki farkları gösteren detaylı bir rapor sunar.

./gradlew verifyRoborazziDebug

Komponent içerisinde renk kodunu değiştirerek test edelim.

val orange = Color(0, 229, 255, 255)

Testimizi verifyRoborazziDebug olarak çalıştıralım. Hata almalıyız hata linki bizi detaylı rapora götürür.

Seçili kısımdan farkların oluşturulduğu görselin eklendiği dosya konumunu görebiliyoruz.

Turuncu komponent kontrol edilip referans almasını istediğimiz. Mavi komponent şu anda uygulamada çalışan komponent. Kırmızı kısımlar ise ikisi arasındaki fark olarak gösteriliyor.

Referans görseller doğru görünüm olduğundan emin olunduğunda kayıt edilmelidir. Her zaman boş bir ekran görüntüsü ile başlayarak testleri gerçekleştirmek iyi bir yoldur. Activity üzerinden yapılabileceği gibi komponent ve gif-lottie için de ekran görüntüsü testleri yapılabilir. Espresso ve diğer test kütüphaneleri eklenerek görünümlere işlevsellik kazandırılabilir.

Ekran görüntü testleri ile uygulamanın görsel ve işlevselliğini kontrol eden süreçler geliştirilme aşamasında veya CI/CD süreçlerinde kolaylıkla test edilerek otomatikleştirilebilir. Böylelikle geliştirme ve entegrasyon süreçleri hızlanır. Manuel test’ler azaltılabilir.

Tam koda buradan ulaşabilirsiniz.

Görüş, öneri veya eleştirileriniz için LinkedIn veya Twitter’dan ulaşabilirsiniz

--

--