Dokumen ini menjelaskan cara menyelesaikan tugas pengujian otomatis umum menggunakan Espresso API.
Espresso API mendorong penulis pengujian untuk memikirkan apa yang mungkin
dilakukan pengguna saat berinteraksi dengan aplikasi, yaitu menemukan elemen UI dan berinteraksi
dengannya. Pada saat yang sama, framework mencegah akses langsung ke aktivitas
dan tampilan aplikasi karena menyimpan objek tersebut dan mengoperasikannya
dari UI thread merupakan sumber utama kegagalan pengujian. Dengan demikian, Anda tidak akan
melihat metode seperti getView()
dan getCurrentActivity()
di Espresso API.
Anda masih dapat beroperasi pada tampilan dengan aman dengan mengimplementasikan subclass
ViewAction
dan ViewAssertion
Anda sendiri.
Komponen API
Komponen utama Espresso meliputi:
- Espresso – Titik entri ke interaksi dengan tampilan (melalui
onView()
danonData()
). Selain itu, mengekspos API yang tidak selalu terikat ke tampilan mana pun, sepertipressBack()
. - ViewMatchers – Kumpulan objek yang mengimplementasikan
antarmuka
Matcher<? super View>
. Anda dapat meneruskan satu atau beberapa objek tersebut ke metodeonView()
untuk menemukan tampilan dalam hierarki tampilan saat ini. - ViewActions – Kumpulan objek
ViewAction
yang dapat diteruskan ke metodeViewInteraction.perform()
, seperticlick()
. - ViewAssertions - Kumpulan objek
ViewAssertion
yang dapat diteruskan ke metodeViewInteraction.check()
. Biasanya, Anda akan menggunakan pernyataan yang cocok, yang menggunakan pencocok View untuk menegaskan status tampilan yang saat ini dipilih.
Contoh:
Kotlin
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()))
Java
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()));
Menemukan tampilan
Pada sebagian besar kasus, metode onView()
mengambil hamcrest matcher
yang diharapkan cocok dengan satu — dan hanya satu — tampilan dalam hierarki tampilan
saat ini. Pencocok sangat andal dan akan familier bagi mereka yang telah menggunakannya
dengan Mockito atau JUnit. Jika tidak terbiasa dengan hamcrest matcher, sebaiknya Anda mulai dengan melihat sekilas presentasi
ini.
Sering kali tampilan yang diinginkan memiliki R.id
unik dan pencocok withId
sederhana akan
mempersempit penelusuran tampilan. Namun, ada banyak kasus saat Anda
tidak dapat menentukan R.id
pada waktu pengembangan pengujian. Misalnya, tampilan spesifik
mungkin tidak memiliki R.id
atau R.id
-nya tidak unik. Hal ini dapat mengakibatkan uji instrumentasi normal menjadi rapuh dan rumit untuk ditulis karena cara normal untuk
mengakses tampilan—dengan findViewById()
—tidak berfungsi. Dengan demikian, Anda mungkin
perlu mengakses anggota pribadi Aktivitas atau Fragmen yang menyimpan tampilan, atau
menemukan penampung dengan R.id
yang diketahui dan membuka kontennya untuk
tampilan tertentu.
Espresso menangani masalah ini dengan mudah dengan memungkinkan Anda mempersempit tampilan
menggunakan objek ViewMatcher
yang sudah ada atau objek kustom Anda sendiri.
Menemukan tampilan berdasarkan R.id
-nya semudah memanggil onView()
:
Kotlin
onView(withId(R.id.my_view))
Java
onView(withId(R.id.my_view));
Terkadang, nilai R.id
digunakan bersama untuk beberapa tampilan. Jika hal ini terjadi,
upaya untuk menggunakan R.id
tertentu akan memberi Anda pengecualian, seperti
AmbiguousViewMatcherException
. Pesan pengecualian memberi Anda representasi teks
dari hierarki tampilan saat ini, yang dapat Anda telusuri dan temukan
tampilan yang cocok dengan R.id
non-unik:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
Melihat berbagai atribut tampilan, Anda mungkin menemukan
properti yang dapat diidentifikasi secara unik. Pada contoh di atas, salah satu tampilan memiliki teks
"Hello!"
. Anda dapat menggunakan ini untuk mempersempit penelusuran menggunakan pencocok kombinasi:
Kotlin
onView(allOf(withId(R.id.my_view), withText("Hello!")))
Java
onView(allOf(withId(R.id.my_view), withText("Hello!")));
Anda juga dapat memilih untuk tidak membalikkan matcher apa pun:
Kotlin
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))
Java
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))));
Lihat ViewMatchers
untuk matcher tampilan yang disediakan oleh Espresso.
Pertimbangan
- Dalam aplikasi yang berperilaku baik, semua tampilan yang dapat berinteraksi dengan pengguna
harus berisi teks deskriptif atau memiliki deskripsi konten. Lihat
Membuat aplikasi lebih mudah diakses untuk detail
selengkapnya. Jika Anda tidak dapat mempersempit penelusuran menggunakan
withText()
atauwithContentDescription()
, pertimbangkan untuk memperlakukannya sebagai bug aksesibilitas. - Gunakan matcher paling tidak deskriptif yang menemukan satu tampilan yang Anda
cari. Jangan terlalu menentukannya karena hal ini akan memaksa framework untuk melakukan lebih banyak pekerjaan daripada
yang diperlukan. Misalnya, jika tampilan secara unik dapat diidentifikasi oleh teksnya, Anda
tidak perlu menentukan bahwa tampilan juga dapat ditetapkan dari
TextView
. Untuk banyak tampilan,R.id
tampilan seharusnya cukup. - Jika tampilan target ada di dalam
AdapterView
—sepertiListView
,GridView
, atauSpinner
—metodeonView()
mungkin tidak berfungsi. Dalam kasus ini, Anda harus menggunakanonData()
sebagai gantinya.
Menjalankan tindakan pada tampilan
Setelah menemukan matcher yang sesuai untuk tampilan target, Anda dapat
menjalankan instance ViewAction
pada tampilan tersebut menggunakan metode perform.
Misalnya, untuk mengklik tampilan:
Kotlin
onView(...).perform(click())
Java
onView(...).perform(click());
Anda dapat menjalankan lebih dari satu tindakan dengan satu panggilan perform:
Kotlin
onView(...).perform(typeText("Hello"), click())
Java
onView(...).perform(typeText("Hello"), click());
Jika tampilan yang Anda kerjakan terletak di dalam ScrollView
(vertikal atau
horizontal), pertimbangkan tindakan sebelumnya yang mengharuskan tampilan
ditampilkan—seperti click()
dan typeText()
—dengan scrollTo()
. Hal ini
memastikan bahwa tampilan akan ditampilkan sebelum melanjutkan ke tindakan lain:
Kotlin
onView(...).perform(scrollTo(), click())
Java
onView(...).perform(scrollTo(), click());
Lihat ViewActions
untuk tindakan tampilan yang disediakan oleh Espresso.
Memeriksa pernyataan tampilan
Pernyataan dapat diterapkan ke tampilan yang saat ini dipilih dengan metode
check()
. Pernyataan yang paling sering digunakan adalah pernyataan matches()
. Class ini menggunakan
objek ViewMatcher
untuk menyatakan status tampilan yang saat ini dipilih.
Misalnya, untuk memeriksa apakah tampilan memiliki teks "Hello!"
:
Kotlin
onView(...).check(matches(withText("Hello!")))
Java
onView(...).check(matches(withText("Hello!")));
Jika Anda ingin menyatakan bahwa "Hello!"
adalah konten tampilan, kode berikut ini dianggap praktik yang tidak baik:
Kotlin
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()))
Java
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));
Di sisi lain, jika Anda ingin menyatakan bahwa tampilan dengan teks "Hello!"
terlihat—misalnya setelah perubahan tanda visibilitas tampilan—
kode tersebut akan baik-baik saja.
Pengujian sederhana pernyataan tampilan
Dalam contoh ini, SimpleActivity
berisi Button
dan TextView
. Saat
tombol diklik, konten TextView
berubah menjadi "Hello Espresso!"
.
Berikut cara mengujinya dengan Espresso:
Klik pada tombol
Langkah pertama adalah mencari properti yang membantu menemukan tombol. Tombol
di SimpleActivity
memiliki R.id
yang unik, seperti yang diharapkan.
Kotlin
onView(withId(R.id.button_simple))
Java
onView(withId(R.id.button_simple));
Sekarang untuk menjalankan klik:
Kotlin
onView(withId(R.id.button_simple)).perform(click())
Java
onView(withId(R.id.button_simple)).perform(click());
Verifikasi teks TextView
TextView
dengan teks yang akan diverifikasi juga memiliki R.id
unik:
Kotlin
onView(withId(R.id.text_simple))
Java
onView(withId(R.id.text_simple));
Sekarang, untuk memverifikasi teks konten:
Kotlin
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")))
Java
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));
Memeriksa pemuatan data dalam tampilan adapter
AdapterView
adalah jenis widget khusus yang memuat datanya secara dinamis dari
Adaptor. Contoh AdapterView
yang paling umum adalah ListView
. Berbeda dengan widget statis seperti LinearLayout
, hanya subset turunan AdapterView
yang dapat dimuat ke dalam hierarki tampilan saat ini. Penelusuran
onView()
sederhana tidak akan menemukan tampilan yang saat ini tidak dimuat.
Espresso menangani hal ini dengan menyediakan titik entri onData()
terpisah yang
dapat terlebih dahulu memuat item adaptor yang dimaksud, sehingga menjadikannya fokus sebelum
mengoperasikannya atau turunannya.
Peringatan: Implementasi kustom
AdapterView
dapat menimbulkan masalah dengan metode
onData()
jika implementasi tersebut menghentikan kontrak pewarisan, khususnya
getItem()
API. Dalam kasus tersebut, tindakan terbaiknya adalah
memfaktorkan ulang kode aplikasi Anda. Jika tidak dapat melakukannya, Anda dapat mengimplementasikan
AdapterViewProtocol
kustom yang cocok. Untuk mengetahui informasi selengkapnya, lihat
class
AdapterViewProtocols
default
yang disediakan oleh Espresso.
Pengujian sederhana tampilan adapter
Pengujian sederhana ini menunjukkan cara menggunakan onData()
. SimpleActivity
berisi
Spinner
dengan beberapa item yang mewakili jenis minuman kopi. Saat
item dipilih, akan ada TextView
yang berubah menjadi "One %s a day!"
, dengan
%s
mewakili item yang dipilih.
Sasaran pengujian ini adalah untuk membuka Spinner
, memilih item tertentu, dan
memverifikasi bahwa TextView
berisi item tersebut. Karena class Spinner
didasarkan pada
AdapterView
, sebaiknya gunakan onData()
, bukan onView()
, untuk
mencocokkan item.
Membuka pemilihan item
Kotlin
onView(withId(R.id.spinner_simple)).perform(click())
Java
onView(withId(R.id.spinner_simple)).perform(click());
Memilih item
Untuk pemilihan item, Spinner
membuat ListView
bersama kontennya.
Tampilan ini bisa sangat panjang, dan elemen mungkin tidak dikontribusikan ke hierarki
tampilan. Dengan menggunakan onData()
, kita memaksa elemen yang diinginkan ke dalam hierarki
tampilan. Item dalam Spinner
adalah string, jadi kita ingin mencocokkan item
yang sama dengan String "Americano"
:
Kotlin
onData(allOf(`is`(instanceOf(String::class.java)), `is`("Americano"))).perform(click())
Java
onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());
Memverifikasi bahwa teks benar
Kotlin
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))))
Java
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))));
Proses Debug
Espresso memberikan informasi debug yang berguna saat pengujian gagal:
Logging
Espresso mencatat semua tindakan tampilan ke logcat. Contoh:
ViewInteraction: Performing 'single click' action on view with text: Espresso
Hierarki tampilan
Espresso menampilkan hierarki tampilan dalam pesan pengecualian jika onView()
gagal.
- Jika
onView()
tidak menemukan tampilan target,NoMatchingViewException
akan ditampilkan. Anda dapat memeriksa hierarki tampilan di string pengecualian untuk menganalisis mengapa matcher tidak cocok dengan tampilan mana pun. - Jika
onView()
menemukan beberapa tampilan yang cocok dengan matcher yang diberikan,AmbiguousViewMatcherException
akan ditampilkan. Hierarki tampilan ditampilkan dan semua tampilan yang cocok akan ditandai dengan labelMATCHES
:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
Saat menangani hierarki tampilan yang rumit atau perilaku widget yang tidak terduga, sebaiknya gunakan Hierarchy Viewer di Android Studio untuk mendapatkan penjelasan.
Peringatan tampilan adapter
Espresso memperingatkan pengguna tentang keberadaan widget AdapterView
. Saat operasi onView()
menampilkan widget NoMatchingViewException
dan AdapterView
ada dalam hierarki tampilan, solusi yang paling umum adalah menggunakan onData()
.
Pesan pengecualian akan menyertakan peringatan dengan daftar tampilan adapter.
Anda dapat menggunakan informasi ini saat memanggil onData()
untuk memuat tampilan target.
Referensi lainnya
Untuk mengetahui informasi selengkapnya tentang penggunaan Espresso dalam pengujian Android, lihat referensi berikut.
Contoh
- CustomMatcherSample:
Menampilkan cara memperluas Espresso agar cocok dengan properti petunjuk objek
EditText
. - RecyclerViewSample:
Tindakan
RecyclerView
untuk Espresso. - (lainnya...)