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
- Klik URL yang diberikan. Tindakan ini akan membuka halaman GitHub project di browser.
- Di halaman GitHub project, klik tombol Code yang akan menampilkan dialog.
- Di dialog, klik tombol Download ZIP untuk menyimpan project di komputer. Tunggu download selesai.
- Temukan file di komputer Anda (mungkin di folder Downloads).
- Klik dua kali pada file ZIP untuk mengekstraknya. Tindakan ini akan membuat folder baru yang berisi file project.
Membuka project di Android Studio
- Mulai Android Studio.
- Di jendela Welcome to Android Studio, klik Open an existing Android Studio project.
Catatan: Jika Android Studio sudah terbuka, pilih opsi menu File > New > Import Project.
- Di dialog Import Project, buka lokasi folder project yang telah diekstrak (kemungkinan ada di folder Downloads).
- Klik dua kali pada folder project tersebut.
- Tunggu Android Studio membuka project.
- Klik tombol Run untuk membangun dan menjalankan aplikasi. Pastikan aplikasi dibangun seperti yang diharapkan.
- 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.
- Tambahkan direktori
test/res
. Klik kanan direktorisrc
pada tampilan project lalu pilih New -> Directory dari menu dropdown.
- Pada jendela pop-up yang dihasilkan, scroll ke bawah lalu pilih
test/res
.
- Dari tampilan project, klik kanan direktori
test/res
lalu pilih New -> File.
- Beri nama file
mars_photos.json
.
- 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"
}
]
- 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.
- 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"
}
- Sekarang buat direktori pengujian reguler.
- Dalam direktori pengujian, buat paket baru
com.example.android.marsphotos
. - Dalam paket, buat file Kotlin baru bernama
BaseTest.kt
.
- 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. Fungsienqueue()
di class ini akan mengurai data dari filemars_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.
- Di class pengujian, buat variabel
lateinit
untukMarsApiService
.
private lateinit var service: MarsApiService
Sekarang, kita memerlukan fungsi yang menentukan variabel service
sebelum melakukan setiap pengujian.
- 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.
- Buat fungsi pengujian bernama
api_service()
. Dalam fungsi pengujian Anda, panggil metodeenqueue
seperti ini:
enqueue("mars_photos.json")
- Selanjutnya, kita memanggil fungsi
getPhotos()
dariMarsApiService
secara langsung. Ingat bahwagetPhotos()
adalah fungsi penangguhan, dan harus dipanggil dari cakupan coroutine. Kita melakukannya dengan menggabungkan panggilan ke metode dirunBlocking
seperti ini:
runBlocking {
val apiResponse = service.getPhotos()
}
- Sekarang, pastikan respons
getPhotos()
yang kita peroleh bukanlahnull
. Perlu diingat karena kita telah menentukanapiResponse
di dalamrunBlocking
, maka kita juga harus mengaksesnya di dalamrunBlocking
.
runBlocking {
val apiResponse = service.getPhotos()
assertNotNull(apiResponse)
}
getPhotos()
menampilkan daftar yang berisi objek MarsPhoto
. Jadi, pastikan daftar tersebut berisi ukuran yang diharapkan.
- 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:
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.