Dasar-dasar pengujian aplikasi Android

Halaman ini menguraikan prinsip inti pengujian aplikasi Android, termasuk praktik terbaik terpusat dan manfaatnya.

Manfaat pengujian

Pengujian adalah bagian integral dari proses pengembangan aplikasi. Dengan menjalankan pengujian terhadap aplikasi secara konsisten, Anda dapat memverifikasi ketepatan, perilaku fungsional, dan kegunaan aplikasi sebelum merilisnya ke publik.

Anda dapat menguji aplikasi secara manual dengan menjelajahinya. Anda mungkin menggunakan perangkat dan emulator yang berbeda, mengubah bahasa sistem, dan mencoba memunculkan setiap error pengguna atau menjelajahi setiap alur pengguna.

Namun, pengujian manual diskalakan dengan buruk, dan mudah untuk mengabaikan regresi dalam perilaku aplikasi. Pengujian otomatis melibatkan penggunaan alat yang melakukan pengujian untuk Anda, yang lebih cepat, lebih dapat diulang, dan umumnya memberikan lebih banyak masukan yang dapat ditindaklanjuti tentang aplikasi di awal proses pengembangan.

Jenis pengujian di Android

Aplikasi seluler bersifat kompleks dan harus berfungsi dengan baik di berbagai lingkungan. Dengan demikian, ada banyak jenis pengujian.

Subjek

Misalnya, ada berbagai jenis pengujian bergantung pada subject:

  • Pengujian fungsional: apakah aplikasi saya melakukan apa yang seharusnya?
  • Pengujian performa: apakah ini melakukannya dengan cepat dan efisien?
  • Pengujian aksesibilitas: apakah pengujian ini berfungsi baik dengan layanan aksesibilitas?
  • Pengujian kompatibilitas: apakah ini berfungsi dengan baik di setiap perangkat dan level API?

Cakupan

Pengujian juga bervariasi bergantung pada ukuran, atau derajat isolasi:

  • Pengujian unit atau pengujian kecil hanya memverifikasi sebagian kecil aplikasi, seperti metode atau class.
  • Pengujian menyeluruh atau pengujian besar memverifikasi bagian aplikasi yang lebih besar secara bersamaan, seperti seluruh layar atau alur penggunaan.
  • Pengujian sedang berada di antara dan periksa integrasi antara dua atau beberapa unit.
Pengujian bisa kecil, sedang, atau besar.
Gambar 1: Menguji cakupan dalam aplikasi biasa.

Ada banyak cara untuk mengklasifikasikan pengujian. Namun, perbedaan yang paling penting bagi developer aplikasi adalah tempat pengujian dijalankan.

Pengujian berinstrumen versus lokal

Anda dapat menjalankan pengujian di perangkat Android atau komputer lain:

  • Pengujian berinstrumen berjalan pada perangkat Android, baik fisik maupun emulasi. Aplikasi di-build dan diinstal bersama aplikasi pengujian yang memasukkan perintah dan membaca statusnya. Pengujian berinstrumen biasanya merupakan pengujian UI, meluncurkan aplikasi, lalu berinteraksi dengannya.
  • Pengujian lokal dijalankan di mesin pengembangan atau server Anda, sehingga disebut juga pengujian sisi host. Pengujian ini biasanya kecil dan cepat, sehingga mengisolasi subjek yang sedang diuji dari bagian aplikasi lainnya.
Pengujian bisa berjalan sebagai uji instrumentasi pada perangkat, atau pengujian lokal pada mesin pengembangan Anda.
Gambar 2: Berbagai jenis pengujian bergantung pada tempat pengujian tersebut dijalankan.

Tidak semua pengujian unit bersifat lokal, dan tidak semua pengujian menyeluruh berjalan pada perangkat. Contoh:

  • Pengujian lokal besar: Anda dapat menggunakan simulator Android yang berjalan secara lokal, seperti Robolectric.
  • Pengujian berinstrumen kecil: Anda dapat memverifikasi bahwa kode Anda berfungsi dengan baik dengan fitur framework, seperti database SQLite. Anda dapat menjalankan pengujian ini di beberapa perangkat untuk memeriksa integrasi dengan beberapa versi SQLite.

Contoh

Cuplikan berikut menunjukkan cara berinteraksi dengan UI dalam pengujian UI berinstrumen yang mengklik elemen dan memverifikasi bahwa elemen lain ditampilkan.

Espresso

// When the Continue button is clicked
onView(withText("Continue"))
    .perform(click())

// Then the Welcome screen is displayed
onView(withText("Welcome"))
    .check(matches(isDisplayed()))

Compose UI

// When the Continue button is clicked
composeTestRule.onNodeWithText("Continue").performClick()

// Then the Welcome screen is displayed
composeTestRule.onNodeWithText("Welcome").assertIsDisplayed()

Cuplikan ini menunjukkan bagian dari pengujian unit untuk ViewModel (pengujian lokal, sisi host):

// Given an instance of MyViewModel
val viewModel = MyViewModel(myFakeDataRepository)

// When data is loaded
viewModel.loadData()

// Then it should be exposing data
assertTrue(viewModel.data != null)

Menentukan strategi pengujian

Dalam kondisi ideal, Anda akan menguji setiap baris kode di aplikasi Anda pada setiap perangkat yang kompatibel dengan aplikasi Anda. Sayangnya, pendekatan ini terlalu lambat dan biaya yang mahal untuk dipraktikkan.

Strategi pengujian yang baik menemukan keseimbangan yang sesuai antara fidelitas, kecepatan, dan keandalan pengujian. Kesamaan lingkungan pengujian dengan perangkat sungguhan menentukan fidelitas pengujian. Pengujian fidelitas lebih tinggi dijalankan pada perangkat yang diemulasi atau perangkat fisik itu sendiri. Pengujian {i>low-fidelity <i}dapat dilakukan di JVM pada workstation lokal. Pengujian {i>high-fidelity<i} sering kali lebih lambat dan memerlukan lebih banyak resource, jadi tidak semua pengujian harus menjadi pengujian {i>high-fidelity.<i}

Pengujian yang tidak stabil

Error terjadi bahkan dalam pengujian yang dirancang dan diterapkan dengan benar. Misalnya, saat menjalankan pengujian di perangkat sungguhan, update otomatis mungkin dimulai di tengah pengujian dan menyebabkannya gagal. Kondisi race yang ringan dalam kode Anda mungkin hanya terjadi pada sebagian kecil waktu. Pengujian yang tidak lulus 100% dari waktu bersifat tidak stabil.

Arsitektur yang dapat diuji

Dengan arsitektur aplikasi yang dapat diuji, kode tersebut mengikuti struktur yang memungkinkan Anda menguji berbagai bagian aplikasi secara mudah secara terpisah. Arsitektur yang dapat diuji memiliki keunggulan lainnya, seperti keterbacaan, pemeliharaan, skalabilitas, dan penggunaan kembali yang lebih baik.

Arsitektur yang tidak dapat diuji menghasilkan hal berikut:

  • Pengujian yang lebih besar, lebih lambat, dan lebih tidak stabil. Class yang tidak dapat diuji unit mungkin harus dicakup oleh pengujian integrasi atau pengujian UI yang lebih besar.
  • Lebih sedikit peluang untuk menguji berbagai skenario. Pengujian yang lebih besar akan lebih lambat, sehingga menguji semua kemungkinan status aplikasi mungkin tidak realistis.

Untuk mempelajari panduan arsitektur lebih lanjut, lihat panduan arsitektur aplikasi.

Pendekatan untuk pemisahan

Jika Anda dapat mengekstrak sebagian fungsi, class, atau modul dari yang lain, pengujiannya akan lebih mudah, dan lebih efektif. Praktik ini dikenal sebagai pemisahan, dan merupakan konsep yang paling penting untuk arsitektur yang dapat diuji.

Teknik pemisahan umum meliputi hal-hal berikut:

  • Memisahkan aplikasi menjadi beberapa lapisan, seperti Presentasi, Domain, dan Data. Anda juga dapat membagi aplikasi menjadi modul, satu per fitur.
  • Hindari menambahkan logika ke entity yang memiliki dependensi besar, seperti aktivitas dan fragmen. Gunakan class ini sebagai titik entri ke framework dan pindahkan UI dan logika bisnis ke tempat lain, seperti ke Composable, ViewModel, atau lapisan domain.
  • Hindari dependensi framework langsung di class yang berisi logika bisnis. Misalnya, jangan gunakan Konteks Android di ViewModels.
  • Permudah untuk mengganti dependensi. Misalnya, gunakan antarmuka, bukan implementasi konkret. Gunakan Injeksi dependensi meskipun Anda tidak menggunakan framework DI.

Langkah berikutnya

Setelah mengetahui alasan Anda harus menguji dan dua jenis pengujian utama, Anda dapat membaca Yang perlu diuji.

Atau, jika Anda ingin membuat pengujian pertama dan belajar sambil melakukannya, lihat Codelab pengujian.