Sifat asinkron aplikasi dan framework seluler sering kali membuat sulit untuk menulis pengujian yang andal dan dapat diulang. Saat peristiwa pengguna dimasukkan, framework pengujian harus menunggu aplikasi selesai bereaksi terhadapnya, yang dapat berkisar dari mengubah beberapa teks di layar hingga pembuatan ulang aktivitas yang lengkap. Jika tidak memiliki perilaku deterministik, pengujian akan tidak stabil.
Framework modern seperti Compose atau Espresso dirancang dengan mempertimbangkan pengujian sehingga ada jaminan tertentu bahwa UI akan tidak ada aktivitas sebelum tindakan atau pernyataan pengujian berikutnya. Ini adalah sinkronisasi.
Menguji sinkronisasi
Masalah masih dapat muncul saat Anda menjalankan operasi latar belakang atau asinkron yang tidak diketahui pengujian, seperti memuat data dari database atau menampilkan animasi tanpa batas.
Untuk meningkatkan keandalan rangkaian pengujian, Anda dapat menginstal cara untuk melacak operasi latar belakang, seperti Resource Nonaktif Espresso. Selain itu, Anda dapat mengganti modul untuk versi pengujian yang dapat Anda kueri untuk mengetahui waktu tidak ada aktivitas atau yang meningkatkan sinkronisasi, seperti TestDispatcher untuk coroutine atau RxIdler untuk RxJava.
Cara meningkatkan stabilitas
Pengujian besar dapat mendeteksi banyak regresi secara bersamaan karena menguji beberapa komponen aplikasi. Pengujian besar biasanya berjalan di emulator atau perangkat, yang berarti memiliki fidelitas tinggi. Meskipun pengujian menyeluruh yang besar memberikan cakupan yang komprehensif, pengujian tersebut lebih rentan terhadap kegagalan sesekali.
Langkah utama yang dapat Anda lakukan untuk mengurangi ketidakstabilan adalah sebagai berikut:
- Mengonfigurasi perangkat dengan benar
- Mencegah masalah sinkronisasi
- Mengimplementasikan percobaan ulang
Untuk membuat pengujian besar menggunakan Compose atau Espresso, Anda biasanya memulai salah satu aktivitas dan menavigasi seperti yang dilakukan pengguna, memverifikasi bahwa UI berperilaku dengan benar menggunakan pernyataan atau pengujian screenshot.
Framework lain, seperti UI Automator, memungkinkan cakupan yang lebih besar, karena Anda dapat berinteraksi dengan UI sistem dan aplikasi lainnya. Namun, pengujian UI Automator mungkin memerlukan lebih banyak sinkronisasi manual sehingga cenderung kurang andal.
Mengonfigurasi perangkat
Pertama, untuk meningkatkan keandalan pengujian, Anda harus memastikan bahwa sistem operasi perangkat tidak mengganggu eksekusi pengujian secara tidak terduga. Misalnya, saat dialog update sistem ditampilkan di atas aplikasi lain atau saat ruang di disk tidak memadai.
Penyedia farm perangkat mengonfigurasi perangkat dan emulator mereka sehingga biasanya Anda tidak perlu melakukan tindakan apa pun. Namun, mereka mungkin memiliki perintah konfigurasi mereka sendiri untuk kasus khusus.
Perangkat yang dikelola Gradle
Jika Anda mengelola emulator sendiri, Anda dapat menggunakan perangkat yang dikelola Gradle untuk menentukan perangkat yang akan digunakan untuk menjalankan pengujian:
android {
testOptions {
managedDevices {
localDevices {
create("pixel2api30") {
// Use device profiles you typically see in Android Studio.
device = "Pixel 2"
// Use only API levels 27 and higher.
apiLevel = 30
// To include Google services, use "google".
systemImageSource = "aosp"
}
}
}
}
}
Dengan konfigurasi ini, perintah berikut akan membuat image emulator, memulai instance, menjalankan pengujian, dan mematikannya.
./gradlew pixel2api30DebugAndroidTest
Perangkat yang dikelola Gradle berisi mekanisme untuk mencoba lagi jika terjadi pemutusan koneksi perangkat dan peningkatan lainnya.
Mencegah masalah sinkronisasi
Komponen yang melakukan operasi latar belakang atau asinkron dapat menyebabkan kegagalan pengujian karena pernyataan pengujian dieksekusi sebelum UI siap untuknya. Seiring meningkatnya cakupan pengujian, kemungkinan pengujian menjadi tidak stabil juga meningkat. Masalah sinkronisasi ini adalah sumber utama ketidakstabilan karena framework pengujian perlu menyimpulkan apakah aktivitas selesai dimuat atau apakah harus menunggu lebih lama.
Solusi
Anda dapat menggunakan resource idling Espresso untuk menunjukkan kapan aplikasi sibuk, tetapi sulit untuk melacak setiap operasi asinkron, terutama dalam pengujian menyeluruh yang sangat besar. Selain itu, resource nonaktif dapat sulit diinstal tanpa polusi kode yang sedang diuji.
Daripada memperkirakan apakah aktivitas sibuk atau tidak, Anda dapat membuat pengujian menunggu hingga kondisi tertentu terpenuhi. Misalnya, Anda dapat menunggu hingga teks atau komponen tertentu ditampilkan di UI.
Compose memiliki kumpulan API pengujian sebagai bagian dari
ComposeTestRule
untuk menunggu matcher yang berbeda:
fun waitUntilAtLeastOneExists(matcher: SemanticsMatcher, timeout: Long = 1000L)
fun waitUntilDoesNotExist(matcher: SemanticsMatcher, timeout: Long = 1000L)
fun waitUntilExactlyOneExists(matcher: SemanticsMatcher, timeout: Long = 1000L)
fun waitUntilNodeCount(matcher: SemanticsMatcher, count: Int, timeout: Long = 1000L)
Dan API generik yang menggunakan fungsi apa pun yang menampilkan boolean:
fun waitUntil(timeoutMillis: Long, condition: () -> Boolean): Unit
Contoh penggunaan:
composeTestRule.waitUntilExactlyOneExists(hasText("Continue")</code>)</p></td>
Mekanisme percobaan ulang
Anda harus memperbaiki pengujian yang tidak stabil, tetapi terkadang kondisi yang membuatnya gagal sangat tidak mungkin sehingga sulit direproduksi. Meskipun Anda harus selalu melacak dan memperbaiki pengujian yang tidak stabil, mekanisme percobaan ulang dapat membantu mempertahankan produktivitas developer dengan menjalankan pengujian beberapa kali hingga lulus.
Percobaan ulang harus dilakukan di beberapa tingkat untuk mencegah masalah, seperti:
- Waktu tunggu untuk menyambungkan ke perangkat habis atau koneksi terputus
- Kegagalan satu pengujian
Menginstal atau mengonfigurasi percobaan ulang bergantung pada framework dan infrastruktur pengujian Anda, tetapi mekanisme umumnya mencakup:
- Aturan JUnit yang mencoba ulang pengujian beberapa kali
- Tindakan atau langkah percobaan ulang dalam alur kerja CI Anda
- Sistem untuk memulai ulang emulator saat tidak responsif, seperti perangkat yang dikelola Gradle.