Testle İlgili Temel Bilgiler

Bu codelab, Advanced Android in Kotlin kursunun bir parçasıdır. Bu kurstan en iyi şekilde yararlanmak için codelab'leri sırayla incelemeniz önerilir ancak bu zorunlu değildir. Kursla ilgili tüm codelab'ler Kotlin'de İleri Düzey Android codelab'leri açılış sayfasında listelenir.

Giriş

İlk uygulamanızın ilk özelliğini uyguladığınızda, beklendiği gibi çalıştığını doğrulamak için kodu çalıştırmış olabilirsiniz. Manuel test olsa da bir test gerçekleştirdiniz. Özellik eklemeye ve güncellemeye devam ettikçe kodunuzu çalıştırmaya ve çalıştığını doğrulamaya da devam etmiş olabilirsiniz. Ancak bunu her seferinde manuel olarak yapmak yorucudur, hatalara açıktır ve ölçeklenmez.

Bilgisayarlar ölçeklendirme ve otomasyon konusunda harikadır. Bu nedenle, büyük ve küçük şirketlerdeki geliştiriciler, yazılımlar tarafından çalıştırılan ve kodun çalıştığını doğrulamak için uygulamayı manuel olarak çalıştırmanızı gerektirmeyen otomatik testler yazar.

Bu codelab serisinde, gerçek dünyadaki bir uygulama için test koleksiyonu (test paketi olarak da bilinir) oluşturmayı öğreneceksiniz.

Bu ilk codelab'de Android'de test etmenin temelleri ele alınmaktadır. İlk testlerinizi yazacak ve LiveData ile ViewModel'leri nasıl test edeceğinizi öğreneceksiniz.

Bilmeniz gerekenler

Aşağıdaki konular hakkında bilgi sahibi olmanız gerekir:

Neler öğreneceksiniz?

Aşağıdaki konular hakkında bilgi edineceksiniz:

  • Android'de birim testleri yazma ve çalıştırma
  • Test odaklı geliştirme nasıl kullanılır?
  • Araçlı testler ve yerel testler nasıl seçilir?

Aşağıdaki kitaplıklar ve kod kavramları hakkında bilgi edineceksiniz:

Yapacaklarınız

  • Android'de hem yerel hem de enstrümanlı testleri ayarlama, çalıştırma ve yorumlama
  • JUnit4 ve Hamcrest kullanarak Android'de birim testleri yazın.
  • Basit LiveData ve ViewModel testleri yazın.

Bu codelab serisinde, yapılacaklar notları uygulamasıyla çalışacaksınız. Bu uygulama, tamamlanacak görevleri yazmanıza ve bunları bir listede görüntülemenize olanak tanır. Ardından bunları tamamlandı veya tamamlanmadı olarak işaretleyebilir, filtreleyebilir ya da silebilirsiniz.

Bu uygulama Kotlin ile yazılmıştır, birden fazla ekranı vardır, Jetpack bileşenlerini kullanır ve Uygulama mimarisi kılavuzundaki mimariye uygundur. Bu uygulamayı nasıl test edeceğinizi öğrenerek aynı kitaplıkları ve mimariyi kullanan uygulamaları test edebilirsiniz.

Başlamak için kodu indirin:

Zip dosyasını indir

Alternatif olarak, kod için GitHub deposunu klonlayabilirsiniz:

$ git clone https://github.com/googlecodelabs/android-testing.git
$ cd android-testing
$ git checkout starter_code

Bu görevde uygulamayı çalıştıracak ve kod tabanını keşfedeceksiniz.

1. adım: Örnek uygulamayı çalıştırın

TO-DO uygulamasını indirdikten sonra Android Studio'da açıp çalıştırın. Derlenmesi gerekir. Aşağıdaki adımları uygulayarak uygulamayı keşfedin:

  • Kaydırma artı düğmesiyle yeni bir görev oluşturun. Önce başlık, ardından görevle ilgili ek bilgileri girin. Yeşil onay işaretiyle kaydedin.
  • Görev listesinde, yeni tamamladığınız görevin başlığını tıklayın ve açıklamanın geri kalanını görmek için bu görevin ayrıntılar ekranına bakın.
  • Listede veya ayrıntılar ekranında, görevin durumunu Tamamlandı olarak ayarlamak için görevin onay kutusunu işaretleyin.
  • Görevler ekranına geri dönün, filtre menüsünü açın ve görevleri Etkin ve Tamamlandı durumuna göre filtreleyin.
  • Gezinme çekmecesini açın ve İstatistikler'i tıklayın.
  • Genel bakış ekranına dönün ve gezinme çekmecesi menüsünden Tamamlandı durumundaki tüm görevleri silmek için Tamamlananları temizle'yi seçin.

2. adım: Örnek uygulama kodunu inceleyin

Yapılacaklar uygulaması, popüler Architecture Blueprints test ve mimari örneğini (örneğin reactive architecture sürümünü kullanarak) temel alır. Uygulama, Uygulama mimarisi kılavuzundaki mimariye uygundur. Fragments, bir depo ve Room ile ViewModels kullanır. Aşağıdaki örneklerden herhangi birini biliyorsanız bu uygulamanın mimarisi benzerdir:

Uygulamanın genel mimarisini anlamanız, herhangi bir katmandaki mantığı derinlemesine anlamanızdan daha önemlidir.

Bulacağınız paketlerin özeti:

Paket: com.example.android.architecture.blueprints.todoapp

.addedittask

Görev ekleme veya düzenleme ekranı: Görev ekleme veya düzenleme için kullanıcı arayüzü katmanı kodu.

.data

Veri katmanı: Görevlerin veri katmanıyla ilgilidir. Veritabanı, ağ ve depo kodunu içerir.

.statistics

İstatistikler ekranı: İstatistikler ekranı için kullanıcı arayüzü katmanı kodu.

.taskdetail

Görev ayrıntıları ekranı: Tek bir görev için kullanıcı arayüzü katmanı kodu.

.tasks

Görevler ekranı: Tüm görevlerin listesi için kullanıcı arayüzü katmanı kodu.

.util

Yardımcı sınıflar: Uygulamanın çeşitli bölümlerinde kullanılan paylaşılan sınıflar (ör. birden fazla ekranda kullanılan yenilemek için kaydırma düzeni).

Veri katmanı (.data)

Bu uygulama, remote paketinde simüle edilmiş bir ağ oluşturma katmanı ve local paketinde bir veritabanı katmanı içerir. Bu projede, basitlik için ağ katmanı gerçek ağ istekleri oluşturmak yerine yalnızca gecikmeli bir HashMap ile simüle edilir.

DefaultTasksRepository, ağ katmanı ile veritabanı katmanı arasında koordinasyon veya aracılık yapar ve verileri kullanıcı arayüzü katmanına döndürür.

Kullanıcı arayüzü katmanı ( .addedittask, .statistics, .taskdetail, .tasks)

Kullanıcı arayüzü katmanı paketlerinin her biri, bir fragment ve bir görünüm modelinin yanı sıra kullanıcı arayüzü için gereken diğer sınıfları (ör. görev listesi için bir bağdaştırıcı) içerir. TaskActivity, tüm parçaları içeren etkinliktir.

Gezinme

Uygulamanın gezinme özelliği Navigation bileşeni tarafından kontrol edilir. Bu değer nav_graph.xml dosyasında tanımlanır. Gezinme, Event sınıfı kullanılarak görünüm modellerinde tetiklenir. Görünüm modelleri, hangi bağımsız değişkenlerin iletileceğini de belirler. Parçalar Event öğesini gözlemler ve ekranlar arasında gerçek gezinmeyi gerçekleştirir.

Bu görevde ilk testlerinizi çalıştıracaksınız.

  1. Android Studio'da Project (Proje) bölmesini açın ve şu üç klasörü bulun:
  • com.example.android.architecture.blueprints.todoapp
  • com.example.android.architecture.blueprints.todoapp (androidTest)
  • com.example.android.architecture.blueprints.todoapp (test)

Bu klasörler kaynak kümeleri olarak bilinir. Kaynak kümeleri, uygulamanızın kaynak kodunu içeren klasörlerdir. Yeşil renkteki (androidTest ve test) kaynak kümeleri, testlerinizi içerir. Yeni bir Android projesi oluşturduğunuzda varsayılan olarak aşağıdaki üç kaynak kümesini alırsınız. Bunları şöyle sıralayabiliriz:

  • main: Uygulama kodunuzu içerir. Bu kod, oluşturabileceğiniz uygulamanın tüm farklı sürümleri (derleme varyantları olarak bilinir) arasında paylaşılır.
  • androidTest: Araçlı testler olarak bilinen testleri içerir.
  • test: Yerel testler olarak bilinen testleri içerir.

Yerel testler ve enstrümanlı testler arasındaki fark, bunların çalıştırılma şeklindedir.

Yerel testler (test kaynak grubu)

Bu testler, geliştirme makinenizin JVM'sinde yerel olarak çalıştırılır ve emülatör ya da fiziksel cihaz gerektirmez. Bu nedenle hızlı çalışırlar ancak doğrulukları daha düşüktür. Yani gerçek dünyada olduğu gibi davranmazlar.

Android Studio'da yerel testler yeşil ve kırmızı üçgen simgesiyle gösterilir.

Enstrümanlı testler (androidTest kaynak grubu)

Bu testler gerçek veya emüle edilmiş Android cihazlarda çalıştırıldığından gerçek dünyada olacakları yansıtır ancak çok daha yavaştır.

Android Studio'da enstrümantasyonlu testler, yeşil ve kırmızı üçgen simgesi olan bir Android ile gösterilir.

1. adım: Yerel test çalıştırın

  1. test klasörünü ExampleUnitTest.kt dosyasını bulana kadar açın.
  2. Dosyayı sağ tıklayın ve Run ExampleUnitTest'i (ExampleUnitTest'i Çalıştır) seçin.

Ekranın alt kısmındaki Çalıştır penceresinde aşağıdaki çıkışı görürsünüz:

  1. Yeşil onay işaretlerine dikkat edin ve addition_isCorrect adlı bir testin geçtiğini doğrulamak için test sonuçlarını genişletin. Ekleme işleminin beklendiği gibi çalıştığını öğrenmekten memnuniyet duyuyoruz.

2. adım: Testin başarısız olmasını sağlayın

Az önce çalıştırdığınız test aşağıda gösterilmektedir.

ExampleUnitTest.kt

// A test class is just a normal class
class ExampleUnitTest {

   // Each test is annotated with @Test (this is a Junit annotation)
   @Test
   fun addition_isCorrect() {
       // Here you are checking that 4 is the same as 2+2
       assertEquals(4, 2 + 2)
   }
}

Testlerin

  • test kaynak kümelerinden birinde sınıflandırılmış olmalıdır.
  • @Test ek açıklamasıyla başlayan işlevler içerir (her işlev tek bir testtir).
  • genellikle onay ifadeleri içerir.

Android, test için JUnit test kitaplığını (bu codelab'de JUnit4) kullanır. Hem onaylamalar hem de @Test ek açıklaması JUnit'ten gelir.

Onaylama, testinizin temelini oluşturur. Kodunuzun veya uygulamanızın beklendiği gibi çalıştığını kontrol eden bir kod ifadesidir. Bu durumda, 4'ün 2 + 2'ye eşit olup olmadığını kontrol eden assertEquals(4, 2 + 2) ifadesi kullanılıyor.

Başarısız bir testin nasıl göründüğünü görmek için kolayca başarısız olması gerektiğini görebileceğiniz bir onaylama ekleyin. 3'ün 1+1'e eşit olup olmadığını kontrol eder.

  1. assertEquals(3, 1 + 1) öğesini addition_isCorrect testine ekleyin.

ExampleUnitTest.kt

class ExampleUnitTest {

   // Each test is annotated with @Test (this is a Junit annotation)
   @Test
   fun addition_isCorrect() {
       assertEquals(4, 2 + 2)
       assertEquals(3, 1 + 1) // This should fail
   }
}
  1. Testi çalıştırın.
  1. Test sonuçlarında, testin yanında bir X işareti görürsünüz.

  1. Ayrıca şunlara da dikkat edin:
  • Tek bir başarısız onaylama, testin tamamının başarısız olmasına neden olur.
  • Beklenen değer (3) ile gerçekte hesaplanan değer (2) karşılaştırılır.
  • Başarısız onaylama satırına ((ExampleUnitTest.kt:16)) yönlendirilirsiniz.

3. adım: Araçlı bir test çalıştırın

Araçlı testler androidTest kaynak kümesindedir.

  1. androidTest kaynak grubunu açın.
  2. ExampleInstrumentedTest adlı testi çalıştır.

ExampleInstrumentedTest

@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
    @Test
    fun useAppContext() {
        // Context of the app under test.
        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
        assertEquals("com.example.android.architecture.blueprints.reactive",
            appContext.packageName)
    }
}

Yerel testten farklı olarak bu test, bir cihazda (aşağıdaki örnekte emüle edilmiş bir Pixel 2 telefonda) çalışır:

Bir cihaz bağlıysa veya bir emülatör çalışıyorsa testi emülatörde görmeniz gerekir.

Bu görevde, uygulamanızın etkin ve tamamlanmış görev istatistiklerinin yüzdesini hesaplayan getActiveAndCompleteStats için testler yazacaksınız. Bu sayıları uygulamanın istatistikler ekranında görebilirsiniz.

1. adım: Bir test sınıfı oluşturun

  1. main kaynak grubunda, todoapp.statistics içinde StatisticsUtils.kt öğesini açın.
  2. getActiveAndCompletedStats işlevini bulun.

StatisticsUtils.kt

internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

   val totalTasks = tasks!!.size
   val numberOfActiveTasks = tasks.count { it.isActive }
   val activePercent = 100 * numberOfActiveTasks / totalTasks
   val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks

   return StatsResult(
       activeTasksPercent = activePercent.toFloat(),
       completedTasksPercent = completePercent.toFloat()
   )
  
}

data class StatsResult(val activeTasksPercent: Float, val completedTasksPercent: Float)

getActiveAndCompletedStats işlevi, bir görev listesini kabul eder ve StatsResult döndürür. StatsResult, tamamlanan görevlerin yüzdesi ve etkin görevlerin yüzdesi olmak üzere iki sayı içeren bir veri sınıfıdır.

Android Studio, bu işlevin testlerini uygulamanıza yardımcı olacak test saplamaları oluşturma araçları sunar.

  1. getActiveAndCompletedStats simgesini sağ tıklayın ve Oluştur > Test'i seçin.

Test Oluştur iletişim kutusu açılır:

  1. Sınıf adı: bölümünü StatisticsUtilsTest olarak değiştirin (StatisticsUtilsKtTest yerine; test sınıfı adında KT olmaması biraz daha iyi olur).
  2. Diğer varsayılan değerleri koruyun. JUnit 4, uygun test kitaplığıdır. Hedef paket doğru (StatisticsUtils sınıfının konumunu yansıtır) ve onay kutularından herhangi birini işaretlemeniz gerekmez (bu yalnızca ek kod oluşturur ancak testinizi sıfırdan yazarsınız).
  3. Tamam'a basın.

Hedef dizini seçin iletişim kutusu açılır:

İşleviniz matematiksel hesaplamalar yaptığından ve Android'e özgü herhangi bir kod içermediğinden yerel bir test yapacaksınız. Bu nedenle, gerçek veya emüle edilmiş bir cihazda çalıştırmanıza gerek yoktur.

  1. Yerel testler yazacağınız için test dizinini (androidTest değil) seçin.
  2. Tamam'ı tıklayın.
  3. test/statistics/ içinde oluşturulan StatisticsUtilsTest sınıfına dikkat edin.

2. adım: İlk test işlevinizi yazın

Aşağıdakileri kontrol eden bir test yazacaksınız:

  • Tamamlanmış görev yoksa ve bir etkin görev varsa
  • Etkin testlerin yüzdesinin %100 olması,
  • ve tamamlanan görevlerin yüzdesi %0'dır.
  1. StatisticsUtilsTest adlı kişiyi aç.
  2. getActiveAndCompletedStats_noCompleted_returnsHundredZero adlı bir işlev oluşturun.

StatisticsUtilsTest.kt

class StatisticsUtilsTest {

    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {
        // Create an active task

        // Call your function

        // Check the result
    }
}
  1. Test olduğunu belirtmek için işlev adının üzerine @Test ek açıklamasını ekleyin.
  2. Görev listesi oluşturun.
// Create an active task 
val tasks = listOf<Task>(
            Task("title", "desc", isCompleted = false)
        )
  1. Bu görevler için getActiveAndCompletedStats'ı arayın.
// Call your function
val result = getActiveAndCompletedStats(tasks)
  1. Onaylamaları kullanarak result değerinin beklediğiniz değer olduğunu doğrulayın.
// Check the result
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)

Tam kod aşağıda verilmiştir.

StatisticsUtilsTest.kt

class StatisticsUtilsTest {

    @Test
    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {

        // Create an active task (the false makes this active)
        val tasks = listOf<Task>(
            Task("title", "desc", isCompleted = false)
        )
        // Call your function
        val result = getActiveAndCompletedStats(tasks)

        // Check the result
        assertEquals(result.completedTasksPercent, 0f)
        assertEquals(result.activeTasksPercent, 100f)
    }
}
  1. Testi çalıştırın (StatisticsUtilsTest simgesini sağ tıklayıp Çalıştır'ı seçin).

Şu koşulları karşılamalıdır:

3. adım: Hamcrest bağımlılığını ekleyin

Testleriniz, kodunuzun ne yaptığını gösteren bir doküman görevi gördüğünden, testlerinizin okunabilir olması önemlidir. Aşağıdaki iki iddiayı karşılaştırın:

assertEquals(result.completedTasksPercent, 0f)

// versus

assertThat(result.completedTasksPercent, `is`(0f))

İkinci iddia, insan tarafından yazılmış bir cümleye çok daha fazla benziyor. Hamcrest adlı bir onaylama çerçevesi kullanılarak yazılmıştır. Okunabilir onaylamalar yazmak için kullanabileceğiniz bir diğer iyi araç da Truth kitaplığıdır. Bu codelab'de onaylamalar yazmak için Hamcrest'i kullanacaksınız.

  1. build.grade (Module: app) bağlantısını açın ve aşağıdaki bağımlılığı ekleyin.

app/build.gradle

dependencies {
    // Other dependencies
    testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"
}

Genellikle bağımlılık eklerken implementation kullanırsınız ancak burada testImplementation kullanıyorsunuz. Uygulamanızı tüm dünyayla paylaşmaya hazır olduğunuzda, APK'nızın boyutunu uygulamanızdaki test kodu veya bağımlılıklarla şişirmemeniz en iyisidir. Gradle yapılandırmalarını kullanarak bir kitaplığın ana koda mı yoksa test koduna mı dahil edileceğini belirleyebilirsiniz. En yaygın yapılandırmalar şunlardır:

  • implementation—Bağımlılık, test kaynak kümeleri de dahil olmak üzere tüm kaynak kümelerinde kullanılabilir.
  • testImplementation: Bağımlılık yalnızca test kaynağı grubunda kullanılabilir.
  • androidTestImplementation: Bağımlılık yalnızca androidTest kaynak grubunda kullanılabilir.

Hangi yapılandırmayı kullandığınız, bağımlılığın nerede kullanılabileceğini tanımlar. Şunu yazarsanız:

testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"

Bu, Hamcrest'in yalnızca test kaynağı grubunda kullanılabileceği anlamına gelir. Ayrıca, Hamcrest'in nihai uygulamanıza dahil edilmemesini sağlar.

4. adım: Hamcrest'i kullanarak onaylamalar yazın

  1. getActiveAndCompletedStats_noCompleted_returnsHundredZero() testini assertEquals yerine Hamcrest'in assertThat işlevini kullanacak şekilde güncelleyin.
// REPLACE
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)

// WITH
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))

İstenirse içe aktarma import org.hamcrest.Matchers.`is` özelliğini kullanabileceğinizi unutmayın.

Son test aşağıdaki kod gibi görünür.

StatisticsUtilsTest.kt

import com.example.android.architecture.blueprints.todoapp.data.Task
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.`is`
import org.junit.Test

class StatisticsUtilsTest {

    @Test
    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {

        // Create an active tasks (the false makes this active)
        val tasks = listOf<Task>(
            Task("title", "desc", isCompleted = false)
        )
        // Call your function
        val result = getActiveAndCompletedStats(tasks)

        // Check the result
        assertThat(result.activeTasksPercent, `is`(100f))
        assertThat(result.completedTasksPercent, `is`(0f))

    }
}
  1. Güncellenen testinizi çalıştırarak hâlâ çalıştığını doğrulayın.

Bu codelab'de Hamcrest'in tüm ayrıntıları öğretilmeyecektir. Daha fazla bilgi edinmek isterseniz resmi eğitime göz atın.

Bu, pratik için isteğe bağlı bir görevdir.

Bu görevde JUnit ve Hamcrest kullanarak daha fazla test yazacaksınız. Ayrıca, Test Odaklı Geliştirme programı uygulamasından türetilen bir stratejiyi kullanarak testler yazacaksınız. Test odaklı geliştirme (TDD), özellik kodunuzu önce yazmak yerine önce testlerinizi yazmanızı öneren bir programlama yaklaşımıdır. Ardından, testlerinizi geçme hedefiyle özellik kodunuzu yazarsınız.

1. Adım: Testleri yazma

Normal bir görev listeniz olduğunda test yazma:

  1. Tamamlanmış bir görev varsa ve etkin görev yoksa activeTasks yüzdesi 0f, tamamlanmış görevlerin yüzdesi ise 100f olmalıdır .
  2. İki tamamlanmış ve üç etkin görev varsa tamamlanma yüzdesi 40f, etkin görev yüzdesi ise 60f olmalıdır.

2. adım: Bir hata için test yazma

getActiveAndCompletedStats için yazılan kodda hata var. Listenin boş veya null olması durumunda ne olacağını düzgün şekilde ele almadığına dikkat edin. Bu iki durumda da her iki yüzdelik değer sıfır olmalıdır.

internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

   val totalTasks = tasks!!.size
   val numberOfActiveTasks = tasks.count { it.isActive }
   val activePercent = 100 * numberOfActiveTasks / totalTasks
   val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks

   return StatsResult(
       activeTasksPercent = activePercent.toFloat(),
       completedTasksPercent = completePercent.toFloat()
   )
  
}

Kodu düzeltmek ve test yazmak için test odaklı geliştirme yöntemini kullanırsınız. Test odaklı geliştirme aşağıdaki adımları izler.

  1. Given, When, Then yapısını kullanarak ve kurala uygun bir adla testi yazın.
  2. Testin başarısız olduğunu onaylayın.
  3. Testin geçmesi için gereken minimum kodu yazın.
  4. Bu işlemi tüm testler için tekrarlayın.

Hata düzeltmeyle başlamak yerine önce testleri yazarak başlayacaksınız. Ardından, gelecekte bu hataları yanlışlıkla yeniden eklemenizi engelleyecek testleriniz olduğunu onaylayabilirsiniz.

  1. Boş bir liste (emptyList()) varsa her iki yüzde de 0f olmalıdır.
  2. Görevler yüklenirken bir hata oluştuysa liste null olur ve her iki yüzde de 0 olur.
  3. Testlerinizi çalıştırın ve başarısız olduğunu doğrulayın:

3. Adım: Hatayı düzeltme

Testlerinizi yaptıktan sonra hatayı düzeltin.

  1. getActiveAndCompletedStats işlevindeki hatayı, tasks değeri null ise veya boşsa 0f değerini döndürerek düzeltin:
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

    return if (tasks == null || tasks.isEmpty()) {
        StatsResult(0f, 0f)
    } else {
        val totalTasks = tasks.size
        val numberOfActiveTasks = tasks.count { it.isActive }
        StatsResult(
            activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
            completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
        )
    }
}
  1. Testlerinizi tekrar çalıştırın ve tüm testlerin artık başarılı olduğunu doğrulayın.

TDD'yi uygulayarak ve testleri önce yazarak şunların sağlanmasına yardımcı oldunuz:

  • Yeni işlevlerle her zaman ilişkili testler bulunur. Bu nedenle testleriniz, kodunuzun ne yaptığına dair doküman görevi görür.
  • Testleriniz doğru sonuçları kontrol eder ve daha önce gördüğünüz hatalara karşı koruma sağlar.

Çözüm: Daha fazla test yazma

Tüm testleri ve ilgili özellik kodunu aşağıda bulabilirsiniz.

StatisticsUtilsTest.kt

class StatisticsUtilsTest {

    @Test
    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {
        val tasks = listOf(
            Task("title", "desc", isCompleted = false)
        )
        // When the list of tasks is computed with an active task
        val result = getActiveAndCompletedStats(tasks)

        // Then the percentages are 100 and 0
        assertThat(result.activeTasksPercent, `is`(100f))
        assertThat(result.completedTasksPercent, `is`(0f))
    }

    @Test
    fun getActiveAndCompletedStats_noActive_returnsZeroHundred() {
        val tasks = listOf(
            Task("title", "desc", isCompleted = true)
        )
        // When the list of tasks is computed with a completed task
        val result = getActiveAndCompletedStats(tasks)

        // Then the percentages are 0 and 100
        assertThat(result.activeTasksPercent, `is`(0f))
        assertThat(result.completedTasksPercent, `is`(100f))
    }

    @Test
    fun getActiveAndCompletedStats_both_returnsFortySixty() {
        // Given 3 completed tasks and 2 active tasks
        val tasks = listOf(
            Task("title", "desc", isCompleted = true),
            Task("title", "desc", isCompleted = true),
            Task("title", "desc", isCompleted = true),
            Task("title", "desc", isCompleted = false),
            Task("title", "desc", isCompleted = false)
        )
        // When the list of tasks is computed
        val result = getActiveAndCompletedStats(tasks)

        // Then the result is 40-60
        assertThat(result.activeTasksPercent, `is`(40f))
        assertThat(result.completedTasksPercent, `is`(60f))
    }

    @Test
    fun getActiveAndCompletedStats_error_returnsZeros() {
        // When there's an error loading stats
        val result = getActiveAndCompletedStats(null)

        // Both active and completed tasks are 0
        assertThat(result.activeTasksPercent, `is`(0f))
        assertThat(result.completedTasksPercent, `is`(0f))
    }

    @Test
    fun getActiveAndCompletedStats_empty_returnsZeros() {
        // When there are no tasks
        val result = getActiveAndCompletedStats(emptyList())

        // Both active and completed tasks are 0
        assertThat(result.activeTasksPercent, `is`(0f))
        assertThat(result.completedTasksPercent, `is`(0f))
    }
}

StatisticsUtils.kt

internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

    return if (tasks == null || tasks.isEmpty()) {
        StatsResult(0f, 0f)
    } else {
        val totalTasks = tasks.size
        val numberOfActiveTasks = tasks.count { it.isActive }
        StatsResult(
            activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
            completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
        )
    }
}

Test yazma ve çalıştırmanın temellerini çok iyi öğrenmişsiniz. Ardından, temel ViewModel ve LiveData testlerini nasıl yazacağınızı öğreneceksiniz.

Bu codelab'in geri kalanında, çoğu uygulamada yaygın olarak kullanılan iki Android sınıfı (ViewModel ve LiveData) için testler yazmayı öğreneceksiniz.

TasksViewModel için testler yazarak başlarsınız.


Tüm mantığı görünüm modelinde olan ve depo koduna dayanmayan testlere odaklanacaksınız. Depo kodu; eşzamansız kod, veritabanları ve ağ çağrıları içerir. Bunların tümü test karmaşıklığını artırır. Şimdilik bunu yapmayacak ve depodaki hiçbir şeyi doğrudan test etmeyen ViewModel işlevselliği için testler yazmaya odaklanacaksınız.



Yazacağınız test, addNewTask yöntemi çağrıldığında yeni görev penceresini açmak için Event'nin tetiklendiğini kontrol eder. Test edeceğiniz uygulama kodu aşağıda verilmiştir.

TasksViewModel.kt

fun addNewTask() {
   _newTaskEvent.value = Event(Unit)
}

1. Adım: TasksViewModelTest sınıfı oluşturma

StatisticsUtilTest için yaptığınız adımları uygulayarak bu adımda TasksViewModelTest için bir test dosyası oluşturursunuz.

  1. tasks paketinde test etmek istediğiniz sınıfı açın. TasksViewModel.
  2. Kodda sınıf adını sağ tıklayın TasksViewModel -> Oluştur -> Test'i seçin.

  1. Test Oluştur ekranında, varsayılan ayarlarda değişiklik yapmadan Tamam'ı tıklayarak kabul edin.
  2. Hedef Dizin Seçin iletişim kutusunda test dizinini seçin.

2. adım: ViewModel testinizi yazmaya başlama

Bu adımda, addNewTask yöntemini çağırdığınızda yeni görev penceresini açmak için Event tetiklendiğini test etmek üzere bir görünüm modeli testi eklersiniz.

  1. addNewTask_setsNewTaskEvent adlı yeni bir test oluşturun.

TasksViewModelTest.kt

class TasksViewModelTest {

    @Test
    fun addNewTask_setsNewTaskEvent() {

        // Given a fresh TasksViewModel


        // When adding a new task


        // Then the new task event is triggered

    }
    
}

Peki ya uygulama bağlamı?

Test etmek için TasksViewModel örneği oluşturduğunuzda oluşturucu, Application Context gerektirir. Ancak bu testte etkinlikler, kullanıcı arayüzü ve parçalar içeren tam bir uygulama oluşturmuyorsunuz. Peki uygulama bağlamını nasıl elde edeceksiniz?

TasksViewModelTest.kt

// Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(???)

AndroidX Test kitaplıkları, Uygulamalar ve Etkinlikler gibi bileşenlerin testler için tasarlanmış sürümlerini sağlayan sınıflar ve yöntemler içerir. Simüle edilmiş Android çerçeve sınıflarına(ör. Application Context) ihtiyacınız olan bir yerel testiniz olduğunda AndroidX Test'i düzgün şekilde ayarlamak için aşağıdaki adımları uygulayın:

  1. AndroidX Test çekirdek ve ext bağımlılıklarını ekleyin.
  2. Robolectric Testing library bağımlılığını ekleyin.
  3. Sınıfa AndroidJUnit4 test çalıştırıcısıyla not ekleme
  4. AndroidX Test kodu yazma

Bu adımları tamamlayacak ve ardından birlikte ne yaptıklarını anlayacaksınız.

3. Adım: Gradle bağımlılıklarını ekleyin

  1. Temel AndroidX Test ve ext bağımlılıklarının yanı sıra Robolectric test bağımlılığını eklemek için bu bağımlılıkları uygulama modülünüzün build.gradle dosyasına kopyalayın.

app/build.gradle

    // AndroidX Test - JVM testing
testImplementation "androidx.test.ext:junit-ktx:$androidXTestExtKotlinRunnerVersion"

    testImplementation "androidx.test:core-ktx:$androidXTestCoreVersion"

 testImplementation "org.robolectric:robolectric:$robolectricVersion"

4. Adım. JUnit Test Runner'ı ekleme

  1. Test sınıfınızın üzerine @RunWith(AndroidJUnit4::class) ekleyin.

TasksViewModelTest.kt

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
    // Test code
}

5. Adım: AndroidX Test'i kullanma

Bu noktada AndroidX Test kitaplığını kullanabilirsiniz. Bu, bir uygulama bağlamı alan ApplicationProvider.getApplicationContext yöntemini içerir.

  1. AndroidX test kitaplığındaki ApplicationProvider.getApplicationContext() öğesini kullanarak TasksViewModel oluşturun.

TasksViewModelTest.kt

// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
  1. tasksViewModel numaralı telefondan addNewTask kullanıcısını ara.

TasksViewModelTest.kt

tasksViewModel.addNewTask()

Bu noktada testiniz aşağıdaki koda benzemelidir.

TasksViewModelTest.kt

    @Test
    fun addNewTask_setsNewTaskEvent() {

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        // TODO test LiveData
    }
  1. Çalıştığını onaylamak için testinizi çalıştırın.

Kavram: AndroidX Test nasıl çalışır?

AndroidX Test nedir?

AndroidX Test, test için kullanılan bir kitaplık koleksiyonudur. Uygulamalar ve Etkinlikler gibi bileşenlerin testler için tasarlanmış sürümlerini sunan sınıflar ve yöntemler içerir. Örneğin, yazdığınız bu kod, uygulama bağlamını almak için kullanılan bir AndroidX Test işlevine örnektir.

ApplicationProvider.getApplicationContext()

AndroidX Test API'lerinin avantajlarından biri, hem yerel testler hem de enstrümanlı testler için çalışacak şekilde oluşturulmuş olmasıdır. Bu özellik sayesinde:

  • Aynı testi yerel test veya araçlı test olarak çalıştırabilirsiniz.
  • Yerel testler ile enstrümantasyonlu testler için farklı test API'leri öğrenmeniz gerekmez.

Örneğin, kodunuzu AndroidX Test kitaplıklarını kullanarak yazdığınız için TasksViewModelTest sınıfınızı test klasöründen androidTest klasörüne taşıyabilirsiniz ve testler yine de çalışır. getApplicationContext(), yerel test veya enstrümantasyonlu test olarak çalıştırılmasına bağlı olarak biraz farklı işler:

  • Bu bir enstrümanlı testse öykünücü başlatıldığında veya gerçek bir cihaza bağlandığında sağlanan gerçek uygulama bağlamını alır.
  • Yerel bir testse simüle edilmiş bir Android ortamı kullanılır.

Robolectric nedir?

AndroidX Test'in yerel testler için kullandığı simüle edilmiş Android ortamı Robolectric tarafından sağlanır. Robolectric, testler için simüle edilmiş bir Android ortamı oluşturan ve bir emülatörü başlatmaktan veya bir cihazda çalıştırmaktan daha hızlı çalışan bir kitaplıktır. Robolectric bağımlılığı olmadan şu hatayı alırsınız:

@RunWith(AndroidJUnit4::class) ne işe yarar?

Test çalıştırıcı , testleri çalıştıran bir JUnit bileşenidir. Test çalıştırıcı olmadan testleriniz çalışmaz. JUnit tarafından sağlanan ve otomatik olarak aldığınız varsayılan bir test çalıştırıcı vardır. @RunWith, varsayılan test çalıştırıcıyı değiştirir.

AndroidJUnit4 test çalıştırıcısı, AndroidX Test'in testinizi, enstrümantasyonlu veya yerel testler olmasına bağlı olarak farklı şekilde çalıştırmasına olanak tanır.

6. Adım: Robolectric uyarılarını düzeltme

Kodu çalıştırdığınızda Robolectric'in kullanıldığını fark edeceksiniz.

AndroidX Test ve AndroidJunit4 test çalıştırıcısı sayesinde bu işlem, tek bir satır Robolectric kodu yazmanıza gerek kalmadan yapılır.

İki uyarı görebilirsiniz.

  • No such manifest file: ./AndroidManifest.xml
  • "WARN: Android SDK 29 requires Java 9..."

Gradle dosyanızı güncelleyerek No such manifest file: ./AndroidManifest.xml uyarısını düzeltebilirsiniz.

  1. Doğru Android manifestinin kullanılması için gradle dosyanıza aşağıdaki satırı ekleyin. includeAndroidResources seçeneği, birim testlerinizde AndroidManifest dosyanız da dahil olmak üzere Android kaynaklarına erişmenize olanak tanır.

app/build.gradle

    // Always show the result of every unit test when running via command line, even if it passes.
    testOptions.unitTests {
        includeAndroidResources = true

        // ... 
    }

"WARN: Android SDK 29 requires Java 9..." uyarısı daha karmaşıktır. Android Q'da test çalıştırmak için Java 9 gerekir. Bu codelab'de Android Studio'yu Java 9'u kullanacak şekilde yapılandırmaya çalışmak yerine hedef ve derleme SDK'nızı 28'de tutun.

Özet olarak:

  • Saf görünüm modeli testleri, kodları genellikle Android gerektirmediğinden test kaynak kümesine eklenebilir.
  • Uygulamalar ve Etkinlikler gibi bileşenlerin test sürümlerini almak için AndroidX test kitaplığını kullanabilirsiniz.
  • test kaynak kümenizde simüle edilmiş Android kodu çalıştırmanız gerekiyorsa Robolectric bağımlılığını ve @RunWith(AndroidJUnit4::class) ek açıklamasını ekleyebilirsiniz.

Tebrikler! Test çalıştırmak için hem AndroidX test kitaplığını hem de Robolectric'i kullanıyorsunuz. Testiniz tamamlanmadı (henüz bir onaylama ifadesi yazmadınız, yalnızca // TODO test LiveData yazıyor). Bir sonraki adımda LiveData ile onaylama ifadeleri yazmayı öğreneceksiniz.

Bu görevde, LiveData değerini doğru şekilde onaylamayı öğreneceksiniz.

addNewTask_setsNewTaskEvent görünüm modeli testi olmadan kaldığınız yer.

TasksViewModelTest.kt

    @Test
    fun addNewTask_setsNewTaskEvent() {

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        // TODO test LiveData
    }
    

LiveData test etmek için iki işlem yapmanız önerilir:

  1. InstantTaskExecutorRule hareketini kullanın
  2. LiveData gözlemi yapma

1. Adım: InstantTaskExecutorRule'u kullanma

InstantTaskExecutorRule, JUnit kuralıdır. @get:Rule ek açıklamasıyla birlikte kullanıldığında, InstantTaskExecutorRule sınıfındaki bazı kodların testlerden önce ve sonra çalıştırılmasına neden olur (tam kodu görmek için dosyayı görüntülemek üzere Command+B klavye kısayolunu kullanabilirsiniz).

Bu kural, test sonuçlarının eşzamanlı olarak ve tekrarlanabilir bir sırada gerçekleşmesi için tüm Architecture Components ile ilgili arka plan işlerini aynı iş parçacığında çalıştırır. LiveData'nın test edilmesini içeren testler yazdığınızda bu kuralı kullanın.

  1. Architecture Components çekirdek test kitaplığı (bu kuralı içerir) için Gradle bağımlılığını ekleyin.

app/build.gradle

testImplementation "androidx.arch.core:core-testing:$archTestingVersion"
  1. TasksViewModelTest.kt uygulamasını aç
  2. InstantTaskExecutorRule öğesini TasksViewModelTest sınıfına ekleyin.

TasksViewModelTest.kt

class TasksViewModelTest {
    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()
    
    // Other code...
}

2. adım: LiveDataTestUtil.kt sınıfını ekleme

Sonraki adımınız, test ettiğiniz LiveData öğesinin gözlemlendiğinden emin olmaktır.

LiveData kullanırken genellikle bir etkinlik veya parça (LifecycleOwner) LiveData'yı gözlemler.

viewModel.resultLiveData.observe(fragment, Observer {
    // Observer code here
})

Bu gözlem önemlidir. Şunları yapmak için LiveData'da etkin gözlemcilerinizin olması gerekir:

Görünüm modelinizin LiveData için beklenen LiveData davranışını elde etmek istiyorsanız LiveData ile LifecycleOwner değerini gözlemlemeniz gerekir.

Bu durum bir sorun teşkil eder: TasksViewModel testinizde LiveData öğenizi gözlemleyebileceğiniz bir etkinlik veya parça yoktur. Bu sorunu çözmek için observeForever yöntemini kullanabilirsiniz. Bu yöntem, LifecycleOwner'e ihtiyaç duymadan LiveData'ün sürekli olarak gözlemlenmesini sağlar. observeForever yaptığınızda gözlemcinizi kaldırmayı unutmamalısınız. Aksi takdirde gözlemci sızıntısı riskiyle karşı karşıya kalırsınız.

Bu, aşağıdaki koda benzer. İnceleyin:

@Test
fun addNewTask_setsNewTaskEvent() {

    // Given a fresh ViewModel
    val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())


    // Create observer - no need for it to do anything!
    val observer = Observer<Event<Unit>> {}
    try {

        // Observe the LiveData forever
        tasksViewModel.newTaskEvent.observeForever(observer)

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        val value = tasksViewModel.newTaskEvent.value
        assertThat(value?.getContentIfNotHandled(), (not(nullValue())))

    } finally {
        // Whatever happens, don't forget to remove the observer!
        tasksViewModel.newTaskEvent.removeObserver(observer)
    }
}

Testte tek bir LiveData gözlemlemek için çok fazla standart kod yazmanız gerekir. Bu standart metinden kurtulmanın birkaç yolu vardır. Gözlemci eklemeyi kolaylaştırmak için LiveDataTestUtil adlı bir uzantı işlevi oluşturacaksınız.

  1. test kaynak kümenizde LiveDataTestUtil.kt adlı yeni bir Kotlin dosyası oluşturun.


  1. Aşağıdaki kodu kopyalayıp yapıştırın.

LiveDataTestUtil.kt

import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException


@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
    time: Long = 2,
    timeUnit: TimeUnit = TimeUnit.SECONDS,
    afterObserve: () -> Unit = {}
): T {
    var data: T? = null
    val latch = CountDownLatch(1)
    val observer = object : Observer<T> {
        override fun onChanged(o: T?) {
            data = o
            latch.countDown()
            this@getOrAwaitValue.removeObserver(this)
        }
    }
    this.observeForever(observer)

    try {
        afterObserve.invoke()

        // Don't wait indefinitely if the LiveData is not set.
        if (!latch.await(time, timeUnit)) {
            throw TimeoutException("LiveData value was never set.")
        }

    } finally {
        this.removeObserver(observer)
    }

    @Suppress("UNCHECKED_CAST")
    return data as T
}

Bu yöntem oldukça karmaşıktır. Bu işlev, bir gözlemci ekleyen, LiveData değerini alan ve ardından gözlemciyi temizleyen getOrAwaitValue adlı bir Kotlin uzantı işlevi oluşturur. Bu işlev, yukarıda gösterilen observeForever kodunun kısa ve yeniden kullanılabilir bir sürümüdür. Bu sınıfın tam açıklaması için blog yayınımıza göz atın.

3. Adım: Onaylama yazmak için getOrAwaitValue'yu kullanın

Bu adımda getOrAwaitValue yöntemini kullanır ve newTaskEvent tetiklendiğini kontrol eden bir assert ifadesi yazarsınız.

  1. getOrAwaitValue kullanarak newTaskEvent için LiveData değerini alın.
val value = tasksViewModel.newTaskEvent.getOrAwaitValue()
  1. Değerin boş olmadığını onaylayın.
assertThat(value.getContentIfNotHandled(), (not(nullValue())))

Tam test aşağıdaki koda benzemelidir.

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.android.architecture.blueprints.todoapp.getOrAwaitValue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.not
import org.hamcrest.Matchers.nullValue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {

    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()


    @Test
    fun addNewTask_setsNewTaskEvent() {
        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        val value = tasksViewModel.newTaskEvent.getOrAwaitValue()

        assertThat(value.getContentIfNotHandled(), not(nullValue()))


    }

}
  1. Kodunuzu çalıştırın ve testin geçmesini izleyin.

Test yazmayı öğrendiğinize göre şimdi kendi testinizi yazın. Bu adımda, öğrendiğiniz becerileri kullanarak başka bir TasksViewModel testi yazmayı deneyin.

1. Adım: Kendi ViewModel testinizi yazma

setFilterAllTasks_tasksAddViewVisible() yazacaksınız. Bu test, filtre türünüzü tüm görevleri gösterecek şekilde ayarladıysanız Görev ekle düğmesinin görünür olup olmadığını kontrol etmelidir.

  1. Referans olarak addNewTask_setsNewTaskEvent() kullanarak TasksViewModelTest içinde setFilterAllTasks_tasksAddViewVisible() adlı bir test yazın. Bu test, filtreleme modunu ALL_TASKS olarak ayarlar ve tasksAddViewVisible LiveData'nın true olduğunu onaylar.


Başlamak için aşağıdaki kodu kullanın.

TasksViewModelTest

    @Test
    fun setFilterAllTasks_tasksAddViewVisible() {

        // Given a fresh ViewModel

        // When the filter type is ALL_TASKS

        // Then the "Add task" action is visible
        
    }

Not:

  • Tüm görevler için TasksFilterType enum'ı ALL_TASKS.'dır.
  • Görev ekleme düğmesinin görünürlüğü LiveData tasksAddViewVisible. tarafından kontrol edilir.
  1. Testinizi çalıştırın.

2. adım: Testinizi çözümle karşılaştırma

Çözümünüzü aşağıdaki çözümle karşılaştırın.

TasksViewModelTest

    @Test
    fun setFilterAllTasks_tasksAddViewVisible() {

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When the filter type is ALL_TASKS
        tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)

        // Then the "Add task" action is visible
        assertThat(tasksViewModel.tasksAddViewVisible.getOrAwaitValue(), `is`(true))
    }

Aşağıdakileri yapıp yapmadığınızı kontrol edin:

  • Aynı AndroidX ApplicationProvider.getApplicationContext() ifadesini kullanarak tasksViewModel oluşturursunuz.
  • setFiltering yöntemini çağırıp ALL_TASKS filtre türü enum'ını iletirsiniz.
  • getOrAwaitNextValue yöntemini kullanarak tasksAddViewVisible değerinin doğru olduğunu kontrol edersiniz.

3. Adım: @Before kuralı ekleme

Her iki testin başında bir TasksViewModel tanımladığınıza dikkat edin.

TasksViewModelTest

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

Birden fazla test için tekrarlanan kurulum kodunuz olduğunda, kurulum yöntemi oluşturmak ve tekrarlanan kodu kaldırmak için @Before ek açıklamasını kullanabilirsiniz. Bu testlerin tümü TasksViewModel öğesini test edeceğinden ve bir görünüm modeli gerektirdiğinden bu kodu @Before bloğuna taşıyın.

  1. tasksViewModel| adlı bir lateinit örnek değişkeni oluşturun.
  2. setupViewModel adlı bir yöntem oluşturun.
  3. @Before ile not ekleyin.
  4. Görünüm modeli oluşturma kodunu setupViewModel konumuna taşıyın.

TasksViewModelTest

    // Subject under test
    private lateinit var tasksViewModel: TasksViewModel

    @Before
    fun setupViewModel() {
        tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
    }
  1. Kodunuzu çalıştırın.

Uyarı

Aşağıdakileri yapmayın ,

tasksViewModel

tanımıyla birlikte:

val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

Bu durumda, tüm testler için aynı örnek kullanılır. Her testte, test edilen öğenin (bu durumda ViewModel) yeni bir örneği olması gerektiğinden bu durumdan kaçınmalısınız.

TasksViewModelTest için nihai kodunuz aşağıdaki koda benzemelidir.

TasksViewModelTest

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {

    // Subject under test
    private lateinit var tasksViewModel: TasksViewModel

    // Executes each task synchronously using Architecture Components.
    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()

    @Before
    fun setupViewModel() {
        tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
    }


    @Test
    fun addNewTask_setsNewTaskEvent() {

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        val value = tasksViewModel.newTaskEvent.awaitNextValue()
        assertThat(
            value?.getContentIfNotHandled(), (not(nullValue()))
        )
    }

    @Test
    fun getTasksAddViewVisible() {

        // When the filter type is ALL_TASKS
        tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)

        // Then the "Add task" action is visible
        assertThat(tasksViewModel.tasksAddViewVisible.awaitNextValue(), `is`(true))
    }
    
}

Başladığınız kod ile son kod arasındaki farkı görmek için burayı tıklayın.

Tamamlanmış codelab'in kodunu indirmek için aşağıdaki git komutunu kullanabilirsiniz:

$ git clone https://github.com/googlecodelabs/android-testing.git
$ cd android-testing
$ git checkout end_codelab_1


Alternatif olarak, depoyu Zip dosyası olarak indirebilir, dosyayı açıp Android Studio'da açabilirsiniz.

Zip dosyasını indir

Bu codelab'de aşağıdaki konular ele alındı:

  • Android Studio'dan test çalıştırma
  • Yerel (test) ve enstrümantasyon testleri (androidTest) arasındaki fark.
  • JUnit ve Hamcrest kullanarak yerel birim testleri yazma
  • AndroidX Test Kitaplığı ile ViewModel testleri ayarlama.

Udacity kursu:

Android geliştirici belgeleri:

Videolar:

Diğer:

Bu kurstaki diğer codelab'lerin bağlantıları için Advanced Android in Kotlin codelab'lerinin açılış sayfasına bakın.