Menguji Permintaan Jaringan

1. Sebelum memulai

Pada codelab sebelumnya, Anda telah mempelajari cara membuat permintaan jaringan menggunakan Retrofit. Dalam codelab ini, Anda akan mempelajari cara menulis pengujian unit untuk kode jaringan Anda.

Prasyarat

  • Anda telah membuat direktori pengujian di Android Studio.
  • Anda telah menulis pengujian unit dan instrumentasi di Android Studio.
  • Anda telah menambahkan dependensi Gradle ke project Android.

Yang akan Anda pelajari

  • Cara menyimpan resource untuk pengujian.
  • Cara meniru respons API untuk pengujian.
  • Cara menguji layanan Retrofit API.

Yang Anda perlukan

  • Komputer yang dilengkapi Android Studio.
  • Kode solusi untuk aplikasi Mars Photos.

Mendownload kode awal untuk codelab ini

Dalam codelab ini, Anda akan menambahkan uji instrumentasi ke aplikasi Mars Photos dari kode solusi sebelumnya.

Untuk mendapatkan kode codelab ini dan membukanya di Android Studio, lakukan hal berikut.

Mendapatkan kode

  1. Klik URL yang diberikan. Tindakan ini akan membuka halaman GitHub project di browser.
  2. Di halaman GitHub project, klik tombol Code yang akan menampilkan dialog.

5b0a76c50478a73f.png

  1. Di dialog, klik tombol Download ZIP untuk menyimpan project di komputer. Tunggu download selesai.
  2. Temukan file di komputer Anda (mungkin di folder Downloads).
  3. Klik dua kali pada file ZIP untuk mengekstraknya. Tindakan ini akan membuat folder baru yang berisi file project.

Membuka project di Android Studio

  1. Mulai Android Studio.
  2. Di jendela Welcome to Android Studio, klik Open an existing Android Studio project.

36cc44fcf0f89a1d.png

Catatan: Jika Android Studio sudah terbuka, pilih opsi menu File > New > Import Project.

21f3eec988dcfbe9.png

  1. Di dialog Import Project, buka lokasi folder project yang telah diekstrak (kemungkinan ada di folder Downloads).
  2. Klik dua kali pada folder project tersebut.
  3. Tunggu Android Studio membuka project.
  4. Klik tombol Run 11c34fc5e516fb1c.png untuk membangun dan menjalankan aplikasi. Pastikan aplikasi dibangun seperti yang diharapkan.
  5. Cari file project di jendela alat Project untuk melihat cara aplikasi disiapkan.

2. Ringkasan aplikasi awal

Aplikasi Mars Photos terdiri dari satu layar yang menampilkan daftar foto yang diambil menggunakan permintaan jaringan.

Kode awal ini memiliki beberapa variasi tambahan yang terkait dengan pengujian. Kita tidak akan membahasnya terlalu dalam karena materi tersebut berada di luar cakupan codelab ini, tetapi kita harus menyebutnya.

Saat menguji cara aplikasi dalam menangani pengambilan data dari API, cara terbaiknya adalah memberikan data kita sendiri sehingga kita dapat memastikan seperti apa data tersebut. Data dari API dapat berubah sehingga bisa merusak pengujian dan menyebabkan kegagalan. Selain itu, mengandalkan panggilan jaringan nyata menyebabkan kita gagal karena konektivitas jaringan atau kecepatan jaringan yang tidak stabil mengakibatkan pengujian tidak konsisten. Akibatnya, kita akan menghasilkan beberapa data dalam pengujian dan menyimpannya sebagai file JSON dalam direktori test/res. Ini adalah direktori resource, dan mirip dengan direktori resource di kode aplikasi utama. Perbedaannya adalah resource pengujian kita disimpan di test/res.

  1. Tambahkan direktori test/res. Klik kanan direktori src pada tampilan project lalu pilih New -> Directory dari menu dropdown.

95e789a6a8d34185.png

  1. Pada jendela pop-up yang dihasilkan, scroll ke bawah lalu pilih test/res.

eeb4ef7904e60846.png

  1. Dari tampilan project, klik kanan direktori test/res lalu pilih New -> File.

6ea89527d6534c1a.png

  1. Beri nama file mars_photos.json.

c4f463255956ce33.png

  1. Salin kode berikut ke dalam file mars_photos.json:
[
 {
   "id":"424905",
   "img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000MR0044631300503690E01_DXXX.jpg"
 },
 {
   "id":"424906",
   "img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"
 }
]
  1. Agar dapat mengakses file dalam direktori resource ini, direktori resource pengujian harus secara eksplisit ditentukan sebagai direktori "sumber" dalam file build. Tambahkan baris berikut:

app/build.gradle

android {
    ...
    sourceSets {
       test.resources.srcDirs += 'src/test/res'
    }
}

Dengan menambahkan baris tersebut, kita dapat mengakses file resource tanpa harus mengetik jalur lengkap ke file dalam kode setiap kali diakses dalam pengujian. Mengetikkan jalur lengkap sangatlah tidak disarankan untuk pengujian karena jalur file dapat berubah antara perangkat dan sistem operasi.

  1. Dalam file yang sama, tambahkan dependensi berikut dan sinkronkan Gradle.

app/build.gradle

dependencies {
    ...
    testImplementation 'junit:junit:4.12'
    testImplementation "androidx.arch.core:core-testing:2.1.0"
    testImplementation "com.squareup.okhttp3:mockwebserver:4.9.1"
}
  1. Sekarang buat direktori pengujian reguler.

22137bac57bd2d77.png

  1. Dalam direktori pengujian, buat paket baru com.example.android.marsphotos.
  2. Dalam paket, buat file Kotlin baru bernama BaseTest.kt.

481f7a26f0935093.png

  1. Salin dan tempel kode berikut ke dalam file BaseTest.kt. Tidak masalah jika Anda tidak mengenali kodenya. Istilah "Tiruan" muncul berulang kali dalam kode di bawah ini, dan akan dibahas di selama codelab ini. Fungsi enqueue() di class ini akan mengurai data dari file mars_photos.json yang Anda buat sehingga dapat digunakan dalam pengujian yang akan ditulis nanti.

BaseTest.kt

import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okio.buffer
import okio.source

open class BaseTest {

   val mockWebServer = MockWebServer()

   fun enqueue(fileName: String) {
       val inputStream = javaClass.classLoader!!.getResourceAsStream(fileName)
       val buffer = inputStream.source().buffer()

       mockWebServer.enqueue(
           MockResponse()
               .setResponseCode(200)
               .setBody(buffer.readString(Charsets.UTF_8))
       )
   }
}

3. Data tiruan

Dalam codelab ini, kita sangat mengandalkan data tiruan. Dalam konteks pengujian, menirukan berarti kita menyimulasikan nilai yang ditampilkan dari potongan kode. Dalam codelab sebelumnya, kita telah membuat tiruan class. Kita juga bisa membuat tiruan fungsi dan memintanya menampilkan nilai yang telah ditentukan, atau membuat tiruan API untuk menampilkan potongan data yang telah ditentukan. Hal ini berguna untuk melakukan pengujian karena membantu memisahkan kode untuk pengujian. Dalam codelab ini, kita hanya berfokus pada tiruan respons API. Kita akan membahas fungsi tiruan dalam codelab terpisah.

4. Membuat class pengujian unit

Dalam pengujian ini, kita akan menguji layanan API. Pertama, buat class baru dengan nama MarsApiServiceTests.kt.

5. Dependensi

Kode awal untuk codelab ini dilengkapi dengan dependensi yang diperlukan. Namun, ada dependensi penting yang belum kita bahas.

testImplementation "com.squareup.okhttp3:mockwebserver:4.9.1"

Dependensi ini memungkinkan kita membuat server tiruan. Pada dasarnya, server tiruan mencegat permintaan jaringan dan mengubah rutenya untuk menampilkan data tiruan yang telah ditentukan. Kita akan membahas arti tiruan nanti dalam codelab ini.

6. Praktik terbaik

Perhatikan bahwa pengujian ditulis dalam Kotlin yang merupakan bahasa pemrograman berorientasi objek. Ini artinya kita bisa menulis kode berorientasi objek untuk pengujian. Mengingat banyaknya jumlah kode yang akan ditulis untuk pengujian ini, maka kita tidak perlu menggunakan praktik berorientasi objek. Namun, kita akan menerapkannya untuk menunjukkan konsep tersebut.

Anda mungkin telah mengetahui bahwa kode awal telah menyertakan direktori pengujian unit, dan terdapat class di dalamnya yang disebut BaseTest. Jika memiliki potongan kode yang mungkin digunakan untuk beberapa pengujian, kita dapat membuat open class dan meminta class pengujian mewarisi kode tersebut. Perlu diingat bahwa class BaseTest ini hanya memiliki satu metode yang disebut enqueue(), dan kita hanya akan menulis satu class pengujian dalam codelab ini. Saat menulis pengujian, ingat bahwa Anda bisa memperoleh keuntungan dengan menggunakan pemrograman berorientasi objek. Namun, jangan berlebihan menggunakannya jika tidak benar-benar diperlukan.

7. Menulis pengujian permintaan jaringan

Pertama, pastikan class pengujian Anda mewarisi dari BaseTest.

Kita akan menguji MarsApiService secara langsung sehingga kita memerlukan instance. Kita akan menyiapkannya dengan cara yang sama seperti yang telah dilakukan di kode aplikasi, tetapi URL untuk layanan baru ini akan berbeda.

  1. Di class pengujian, buat variabel lateinit untuk MarsApiService.
private lateinit var service: MarsApiService

Sekarang, kita memerlukan fungsi yang menentukan variabel service sebelum melakukan setiap pengujian.

  1. Buat fungsi dengan nama setup() dan anotasikan dengan @Before.
@Before
fun setup() {}

Untuk pengujian ini, kita tidak akan menggunakan URL untuk permintaan jaringan. Di sinilah MockWebServer menjadi berguna.

Data tiruan

Di class BaseTest, tersedia properti yang diberi nama mockWebServer, dan merupakan instance objek MockWebServer. Objek ini akan mencegat permintaan jaringan, tetapi kita harus terlebih dahulu mengarahkan permintaan jaringan ke URL yang akan dicegat.

MockWebServer memiliki fungsi bernama url() yang menentukan URL yang ingin dicegat. Ingat, kita tidak ingin membuat permintaan jaringan yang nyata, kita hanya ingin berpura-pura membuatnya agar bisa menguji kode jaringan dengan data yang dikontrol dalam pengujian itu sendiri. Fungsi url() menggunakan string yang mewakili URL palsu tersebut dan menampilkan objek HttpUrl. Dalam fungsi setup(), tulis baris berikut:

val url = mockWebServer.url("/")

Kita telah menetapkan endpoint URL yang ingin dicegat dan juga telah mengambil objek HttpUrl yang ditampilkan.

Di dalam fungsi ini, buat instance MarsApiService dengan cara yang sama seperti yang dilakukan di MarsApiService dan MarsApiClass (kecuali variabel lazy). Namun, jangan sertakan URL dasar terlebih dahulu. Instance akan terlihat seperti berikut:

service = Retrofit.Builder()
   .addConverterFactory(MoshiConverterFactory.create(
       Moshi.Builder()
           .add(KotlinJsonAdapterFactory())
           .build()
   ))
   .build()
   .create(MarsApiService::class.java)

Sekarang saatnya menetapkan URL dasar. Tambahkan fungsi berikut ke rantai fungsi Retrofit.Builder():

.baseUrl(url)

Fungsi ini akan memberi tahu layanan API bahwa kita ingin merutekan permintaan ke MockWebServer.

service = Retrofit.Builder()
   .baseUrl(url)
   .addConverterFactory(MoshiConverterFactory.create(
       Moshi.Builder()
           .add(KotlinJsonAdapterFactory())
           .build()
   ))
   .build()
   .create(MarsApiService::class.java)

Sekali lagi, inti dari MockWebServer adalah menghindari permintaan jaringan yang sebenarnya ke API sebenarnya. Garis besarnya adalah jika permintaan jaringan sebenarnya dibuat, pengujian akan gagal jika API gagal. Menggunakan API sungguhan akan menguji API itu sendiri, dan kita hanya berfokus pada pengujian kode dalam project Android.

Anda dapat menganggap MockWebServer sebagai API palsu yang menampilkan data yang dibuat sehingga kita harus secara eksplisit memberi tahu MockWebServer apa yang harus ditampilkan sebelum permintaan dibuat. Di sinilah fungsi enqueue() di BaseTest berperan. Jangan terlalu khawatir dengan kode dalam fungsi ini karena kode tersebut berada di luar cakupan codelab ini. Perlu diketahui bahwa fungsi tersebut mengambil file dari resource pengujian dan mengubahnya menjadi respons API palsu.

  1. Buat fungsi pengujian bernama api_service(). Dalam fungsi pengujian Anda, panggil metode enqueue seperti ini:
enqueue("mars_photos.json")
  1. Selanjutnya, kita memanggil fungsi getPhotos() dari MarsApiService secara langsung. Ingat bahwa getPhotos() adalah fungsi penangguhan, dan harus dipanggil dari cakupan coroutine. Kita melakukannya dengan menggabungkan panggilan ke metode di runBlocking seperti ini:
runBlocking {
    val apiResponse = service.getPhotos()
}
  1. Sekarang, pastikan respons getPhotos() yang kita peroleh bukanlah null. Perlu diingat karena kita telah menentukan apiResponse di dalam runBlocking, maka kita juga harus mengaksesnya di dalam runBlocking.
runBlocking {
    val apiResponse = service.getPhotos()
    assertNotNull(apiResponse)
}

getPhotos() menampilkan daftar yang berisi objek MarsPhoto. Jadi, pastikan daftar tersebut berisi ukuran yang diharapkan.

  1. Lihat file mars_photos.json dalam test/res. Buat pernyataan lain untuk memastikan daftar tidak kosong. Kita juga harus membuat pernyataan untuk memeriksa apakah beberapa data sudah benar. Buka test/res/mars_photos.json dan salin salah satu ID. Nyatakan bahwa nilai ID tersebut sama dengan nilai ID dari item daftar yang sesuai.
runBlocking {
   val apiResponse = service.getPhotos()

   assertNotNull(apiResponse)
   assertTrue("The list was empty", apiResponse.isNotEmpty())
   assertEquals("The IDs did not match", "424905", apiResponse[0].id)
}

Sekarang kode akan terlihat seperti ini: 4000883c0d6d47bb.png

8. Kode solusi

9. Selamat

Menguji permintaan jaringan dapat menjadi sangat rumit, dan codelab ini hanya akan membahas sebagian kecil. Setiap pengujian jaringan yang Anda tulis akan bersifat unik untuk API yang digunakan aplikasi dalam mendapatkan data. Dengan makin banyaknya pengalaman yang Anda peroleh dari penggunaan API, Anda akan dapat memperluas pengujian untuk menyimulasikan kegagalan jaringan dan berbagai respons API. Area pengujian ini bisa sangat sulit, dan Anda harus banyak mencoba untuk menjadi seorang ahli. Jadi, pastikan Anda terus berlatih.

Dalam codelab ini, kita telah mempelajari:

  • Cara menerapkan pemrograman berorientasi objek ke pengujian.
  • Cara menyimpan resource di direktori pengujian.
  • Cara memanggil fungsi penangguhan dalam pengujian
  • Cara meniru respons API dalam pengujian.