Android'de Unit Test, Integration Test, UI Test

Geliştirdiğimiz uygulamaları ilk olarak run ederek doğru bir şekilde çalışıp çalışmadığını test ederiz. Ancak bu yöntem büyüyen ve karmaşıklaşan uygulamalarda geliştirme sürecini olumsuz yönde etkilemektedir. Eklenen her fonksiyonun, tasarımsal objenin bu şekilde test edilmesi zaman kaybına neden olduğu gibi yazılımı geliştiren kişi için de ayrı bir iş yükü oluşturmaktadır.


Yazılım Testi bir uygulamanın istekleri karşılayıp karşılamadığını incelemek ve uygulama geliştirme sürecinde oluşabilecek hataları yakalayarak ileri aşamada oluşabilecek hataları engellemeyi hedeflemektedir. Bu da bizi zaman kayıplarından ve ayrı bir iş yükünden kurtarır.


Testler uygulamamızın doğruluğunu, işlevsel davranışlarını ve kullanılabilirliğini test etmekle birlikte:

  • Hatalar hakkında hızlı geri bildirimleri edinme,

  • Uygulama geliştirme sürecinde hatayı erken tespit etme,

  • Daha güvenli bir şekilde kodu yeniden düzenleme,

  • Kararlı bir geliştirme hızı

gibi avantajlar da sunmaktadır. Böylece testler uygulama geliştirme sürecimizin ayrılmaz bir parçası olmaktadır.


Kullanıcılar uygulamamızla etkileşim içindedirler. Örneğin bir Buton’a tıklayarak işlem onaylamak veya kullanıcı adını değiştirmek için kullanılan bir EditText’e bir input girilmesi gibi. Bu durumda uygulamada kullanılan buton'un ve edittext'in doğruluğunu, işlevselliğini ve kullanılabilirliğini test etmeye ihtiyaç duyarız.


Uygulamamız büyüdükçe eklediğimiz farklı özellikleri test edebilmek için çok yönlü, kapsamlı bir test stratejisine ihtiyaç duyarız. Uygulamamızı geliştirirken test odaklı geliştirmeye dikkat etmeliyiz. Uygulamaya eklenen yeni bir özelliğin ne gibi görevler üstlendiğini dikkate almalı, ona göre her birimini, arayüz olarak beklenen performansı ve uygulamanın diğer yapılarıyla etkileşimini test etmeliyiz.


Test Tipleri


Uygulamamızın işlevselliğini değelendirmek için Küçük, Orta ve Büyük seviyeli testler yazarız.


1- Küçük Seviyeli Testler (Birim Testler (Unit Testing))


Uygulamamımızın davranışlarını kontrol eden en küçük birimleri (bir fonksiyon, bir class) test eder. Bu testler uygulamamızın en büyük test parçasını oluşturur. Yerel ortamda (Android Studio) çalıştığı için bu testler oldukça hızlı, küçük ve yüksek düzeyde odaklanılmış testlerdir. Uygulamanız için yazılan toplam testlerin yaklaşık %70'ini oluşturmaktadırlar.


Birim testlerde JUnit ve Mocking kütüphaneleri kullanılmaktadır. JUnit bir birim testi frameworküdür. Mocking ise sahte nesneler (Bir sınıftan oluşturulan nesne gibi davranırlar. Oluşturulan gerçek nesneye göre daha hızlı işlemler gerçekleştirirler ve daha bağımsız hareket ederler) oluşturarak birim testlerimizi yazabilme olanağı sunar.


Bu testlerin dezavantajı gerçek bir cihazda çalıştırılmadığı için verilerin gerçek verilerle çok bağlantılı olmamasıdır. Birim Testler Local ve Instrumented Birim Test olmak üzere ikiye ayrılır.

  • Local Birim Test : Sadece yerel ortamda çalışan birim testlerdir. Bu testler yürütme süresini en aza indirmek için Java Sanal Ortamı(JVM) üzerinde çalışacak şekilde derlenir.

  • Instrumented Birim Test : Bu testler test edilen uygulamanın bağlamı gibi enstrümantasyon bilgilere erişime sahip olup cihaz veya emülatör üzerinde çalışan birim testlerdir. Local birim testlere göre doğruluk oranı daha yüksek olup daha yavaştırlar.


2- Orta Seviyeli Testler (Entegrasyon Testleri (Integration Testing))


Orta seviyeli testler, bir modül içindeki yığının seviyeleri arasındaki etkileşimleri veya ilgili modüller arasındaki etkileşimleri doğrulayan entegrasyon testleridir. Bu testler yerel ortamda değil, gerçek bir cihazda çalıştırılır. Farklı modüller veya bileşenler arasındaki entegrasyonun çalışıp çalışmadığını test etmek içindir. Uygulamanız için yazılan toplam testlerin yaklaşık %20'sini oluşturmaktadır.


Entegrasyon testlerinde Robolectric veya ActivityScenario sınıfı kullanılabilmektedir.


Bu testler bir cihaz ya da emülatörde çalıştığından birim testlere göre daha yavaş çalışırlar.


3- Büyük Seviyeli Testler (Arayüz Testleri (UI Testing))


Bir kullanıcının birden çok modülü kapsayan kullanıcı deneyimini doğrulayan testlerdir. Akışı baştan sona tamamen (uçtan uca) kontrol etmek için bu testler kullanılır. Bu testlerin doğruluk oranı diğerlerine göre daha yüksektir çünkü gercek bir cihazda gerçek hayat kullanımını simüle ederler. Uygulamanız için yazılan toplam testlerin yaklaşık %10'unu oluşturmaktadır.


Arayüz testlerinde Espresso kullanılmaktadır. Espresso arayüz testi frameworküdür.



Bu testler bir cihaz ya da emülatörde çalıştıkları için daha yavaş çalışırlar.


Genel olarak ele aldığımızda aşağıdaki gibi bir dağılım oluşmaktadır. Ancak bu dağılım projenize göre değişiklik gösterebilmektedir.


Buraya kadar testleri niçin kullandığımızı ve neler olduğuna öğrendik. Şimdi ise bir örnek ile bu testleri nasıl yazacağımıza bakacağız.


Test Entegrasyonu


Örneğimizde 2 adet veri girişi ile taban sayıya ekleme ve çıkarma işlemleri yapacağız. Bu basit uygulamanın birim , entegrasyon ve arayüz testlerini yapacağız.


1. Adım: İlk olarak boş bir Android Studio projesi oluşturuyoruz. Projemizin adı Testing_Demo olsun.



2. Adım: Açılan projemize aşağıdaki bağımlılığı ekleyerek arayüz tasarımımızı oluşturmaya başlıyoruz. Gradle Scripts > build.gradle(:app) içindeki bağımlılıklara tasarım için gerekli olan implementation 'com.google.android.material:material:1.2.1' bağımlılığını ekliyoruz.



3. Adım:






Yanda verilen arayüz tasarımını oluşturacağız. activity_main'e geçip arayüzümüzü oluşturuyoruz. İçerisine aşağıda verilen kodu ekleyip devam ediyoruz.


NOT: JUnit ve Espresso bağımlılıkları projeye dahil olarak yer almaktadır. Ayrıca eklemeye gerek yoktur.

















4. Adım : Yapılan tasarıma fonksiyonellik eklemek için Calculator isimli bir class oluşturuyoruz. Ardından MainActivity'de gerekli düzenlemeleri yaparak devam ediyoruz.



5. Adım: Birim testlerimizi yazmaya başlayabiliriz.


Görsel Referansı


Birim testlerimizi yazmadan önce bilmemiz gereken birkaç JUnit kavramı bulunmaktadır. Bunlar @BeforeClass, @Before, @After, @AfterClass ve @Test .


  • @BeforeClass: Bu etiket sınıftaki herhangi bir metoddan önce ve bir kereye mahsus çalışır. Public ve Statik ve Void'dir.

  • @Before: Her @Test'den önce bir kez çalışır. Public ve Void'dir

  • @After: Her @Test'den sonra bir kez çalışır. Public ve Void'dir.

  • @AfterClass: Bu etiket sınıftaki herhangi bir metoddan sonra ve bir kereye mahsus çalışır. Public ve Statik ve Void'dir.

  • @Test :Bu bizim çalıştıracağımız test metodunu belirtir.


Şimdi Unit testlerimizi nerede yazacağımıza bakalım. Aşağıda görüldüğü üzre androidTest ve test olmak üzere iki test klasörümüz bulunmaktadır. Biz birim testlerimizi test altında yazacağız.


Bizi böyle bir test sınıfı ve içinde örnek bir test metodu karşılamakta.



Öncelikle her fonksiyonu test etmek için Calculator sınıfından bir nesneye ihtiyacımız var. Bu nesne yardımıyla fonksiyonlarımızın istediğimiz gibi çalışıp çalışmadığını test edebiliriz. Bu aşamada Local Birim Test yazacağız.


  • setUp() : Burada her @Test metodundan önce "Before notation run" yazmasını hedeflemekteyiz.

  • testAdd() : Bu metodumuz ile Calculator sınıfında yazdığımız add() fonksiyonun doğru çalışıp çalışmadığını kontrol edeceğiz.

  • testSub() : Bu metodumuz ile Calculator sınıfında yazdığımız sub() fonksiyonun doğru çalışıp çalışmadığını kontrol edeceğiz.

  • tearDown() : Burada her @Test metodundan sonra "After notation run" yazmasını hedeflemekteyiz.


Burada add() ve sub() fonksiyonları Integer değerler döndürmektedir. Peki nasıl test edeceğiz? JUnit'in bize sunduğu assertEquals() metodu ile test edeceğiz.


Görsel Referansı


Yukarıda görüldüğü üzere fonksiyonumuz 3 parametre ile çalışmaktadır. Fonksiyondan beklenen değer, olması gereken değer ve bir metin. Fonksiyonumuz beklenen a değeri ile olması gereken b değerine göre a==b olma durumunda test başarılı değil ise verilen metin ile başarısız olur.



Şimdi testimizi çalıştırıp sonucuna bakalım. Testimizi çalıştırmak için bir kaç yöntem bulunmakta. Cihazda, emülatore ihtiyaç duymadan çalıştırabiliriz. Aşağıda yıldızlandığım kısımlarda beyaz yıldız ne çalıştıracağımızı, mavi ve mor yıldızlar ise test sonuçlarını göstermektedir. Beyaz yıldız kısmında çalıştıracağımız testi belirledikten sonra yeşil butona basarak test edebiliriz. Ayrıca sarı yıldızın olduğu sınıf başındaki yeşil run butonunu ya da kırmızı yıldızın olduğu klasöre sağ tıklayarak run edebilirsiniz.



Elde ettiğimiz sonuç aşağıdaki gibi olmaktadır. Her @Test metodundan önce @Before ve sonrasında ise @After çalıştığı görülmektedir.



6. Adım: Entegrasyon testimizi ActivityScenario sınıfı ile gerçekleştireceğiz. Bu testte hedefimiz MainActivity'nin devam eden durumlarını(state) doğrulamak. Entegrasyon

testimizi yazmaya başlamadan önce bağımlılıklarımızı ekleyerek başlıyoruz. Gradle Scripts > build.gradle(:app) içine androidTestImplementation 'androidx.test:core-ktx:1.3.0' bağımlılığını ekliyoruz.


Daha sonra androidTest klasörünün altında IntegrationTest isimli bir class açıyoruz.



Sınıfımızın başına @RunWith (AndroidJUnit4::class) ekliyoruz ve activityi test etmeye başlıyoruz.







Yanda bir Activity'nin yaşam döngüsü verilmiştir. Bizde kendi uygulamamızda bu stateleri test edeceğiz.










Görsel Referansı


Aşağıda testEventCreated() metodunda ilk olarak Activity'i başlatıyoruz (launchActivity). Daha sonra ise moveToState() metodu ile durum() belirtiyoruz.


@Test
fun testEventCreated() {
    val scenario = launchActivity<MainActivity>()
    scenario.moveToState(Lifecycle.State.CREATED)
}


Son olarak testimizi çalıştırıyoruz ve aşağıdaki durum oluşuyor. Ayrıca testinizin doğru çalıştığını da kontrol edebilirsiniz.




7. Adım: Sırada Arayüzü(UI) testlerimiz bulunmakta. Haydi başlayalım. İlk olarak, Gradle Scripts > build.gradle(:app) içindeki bağımlılıklara gerekli olan;


androidTest Implementation 'com.android.support.test:runner:1.0.2'

androidTestImplementation 'com.android.support.test:rules:1.0.2'


bağımlılığını ekliyoruz. Ardından testlerimizi yazmak için androidTest altında yer alan ExampleInstrumentedTest'i açıyoruz. Testlerimizi burada yazacağız.



Görüldüğü üzere bir test metodumuz bulunmaktadır. Metodumuzda test etmek istediğimiz paket isimleri kontrol edilmektedir. Daha sonra test edeceğimiz activity'i belirterek başlıyoruz.



Test kodlarımızı yazmadan önce Espresso'nun temelde 3 bileşenini öğrenelim:

  • ViewMatchers : Görünümü bul.

  • ViewActions : Görünümde bir eylem gerçekleştir.

  • ViewAssertions : İddiayı doğrula.


** Aşağıdaki örneği inceleyelim. onView() metodu ile eşleşmeleri gerçekleştiriyoruz. Daha sonra withId() metodu ile id ile eşleşen görünüm bulunur ve doubleClick() eylemi gerçekleştirilir.

onView(ViewMatchers.withId(R.id.btn_add))
.perform(ViewActions.doubleClick())

** Aşağıdaki bir başka örnekte onView() metodu ile eşleşmeleri gerçekleştiriyoruz. Daha sonra withId() metodu ile id ile eşleşen görünüm bulunur, matches() eylemi gerçekleştirilir ve withText() metodu ile verilen textin görünümdeki ile eşleşip eşleşmediği kontrol edilir.

onView(withId(R.id.txt_result))
    .check(ViewAssertions.matches(ViewMatchers.withText("25")))

**Aşağıdaki örnekte withId() metodu ile id ile eşleşen görünüm bulunur, typeText() eylemi gerçekleştirilir ve closeSoftKeyboard() metodu ile klavye açık ise kapatır.

onView(withId(R.id.etxt_base_number))
    .perform(ViewActions.typeText("10"), ViewActions.closeSoftKeyboard()

Bu şekilde farklı fonksiyonlar yardımı ile farklı durumları test edebiliriz.



Daha sonra bir emülatör veya cihaz ile testimizi çalıştırıyoruz. Cihazımızda aşağıdaki gibi bir görüntü oluşacaktır.



Ayrıca Android Studio'da da aşağıdaki gibi bir sonuç elde edersiniz.


Yazımın sonuna gelirken uygulamanın kaynak kodlarının yer aldığı repoyu ve referans aldığım kaynakları da aşağıya ekliyorum.


Github: https://github.com/ajdakter/android_testing


Bu yazıyı yazarken faydalandığım kaynaklar:

https://developer.android.com/training/testing/fundamentals

https://proandroiddev.com/android-model-view-intent-with-unit-tests-260e9a0cdd64

https://developer.android.com/training/testing/unit-testing#:~:text=Local%20tests%3A%20Unit%20tests%20that,framework%2C%20we%20recommend%20using%20Robolectric.

https://www.journaldev.com/22674/android-unit-testing-junit4

https://halil-ozcan.medium.com/android-local-unit-test-yaz%C4%B1m%C4%B1-a0749f0385f6

https://medium.com/@temidjoy/build-local-unit-tests-for-your-android-apps-5652dd436aaf

https://medium.com/mindorks/learn-unit-testing-in-android-by-building-a-sample-application-23ec2f6340e8

https://mkyong.com/unittest/junit-4-tutorial-1-basic-usage/

https://www.vogella.com/tutorials/AndroidTestingEspresso/article.html

https://testautomationu.applitools.com/espresso-mobile-testing-tutorial/chapter3.html

https://developer.android.com/guide/components/activities/testing#trigger-actions

https://www.codesenior.com/tr/tutorial/Android-Integration-Test-Activity-Scenario-Kullanimi

https://medium.com/stepstone-tech/better-tests-with-androidxs-activityscenario-in-kotlin-part-1-6a6376b713ea


#android #androidtest #uitest #unittest #integrationtest


0 yorum

Son Paylaşımlar

Hepsini Gör

Komünite

Platform

Mobiler.dev Anasayfa
  • Twitter
  • Instagram
  • development_düzenlendi_düzenlendi
  • Youtube
  • slack-icon-black_edited_edited_edited
  • Gri LinkedIn Simge
imageedit_2_9667998092.png
JetBrains Hakkında Detaylı Bilgi Alın

© 2021 by mobiler.dev

Kurumsal Yazar Hesapları

adesso.png
mobilerdevLogo.jpg
Yazarlık Başvurusu Hakkında Bilgi Alın, Başvuru Yapın.
Topluluk Yazarlarını Tanıyın