Dasar-dasar Espresso

Dokumen ini menjelaskan cara menyelesaikan tugas pengujian otomatis yang umum menggunakan Espresso API.

Espresso API mendorong penulis pengujian untuk memikirkan apa yang mungkin dilakukan pengguna lakukan saat berinteraksi dengan aplikasi - menemukan elemen UI dan berinteraksi dengan mereka. Pada saat yang sama, framework mencegah akses langsung ke aktivitas dan tampilan aplikasi karena berpegang pada objek ini dan pada UI thread, merupakan sumber utama kegagalan pengujian. Dengan demikian, Anda akan tidak melihat metode seperti getView() dan getCurrentActivity() di Espresso API. Anda masih dapat beroperasi dengan aman pada tampilan dengan mengimplementasikan subclass Anda sendiri ViewAction dan ViewAssertion.

Komponen API

Komponen utama Espresso meliputi:

  • Espresso – Titik entri ke interaksi dengan tampilan (melalui onView() dan onData()). Juga mengekspos API yang tidak selalu terikat ke tampilan apa pun, seperti sebagai pressBack().
  • ViewMatchers – Kumpulan objek yang mengimplementasikan Antarmuka Matcher<? super View>. Anda bisa meneruskan satu atau beberapa ID ini ke Metode onView() untuk menemukan tampilan dalam hierarki tampilan saat ini.
  • ViewActions – Kumpulan objek ViewAction yang dapat diteruskan ke metode ViewInteraction.perform(), seperti click().
  • ViewAssertions – Kumpulan objek ViewAssertion yang dapat meneruskan metode ViewInteraction.check(). Sering kali, Anda akan menggunakan pernyataan yang cocok, yang menggunakan pencocok View untuk menegaskan status tampilan yang dipilih saat ini.

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

Dalam sebagian besar kasus, metode onView() mengambil hamcrest matcher yang diharapkan cocok dengan satu — dan hanya satu — tampilan dalam tampilan saat ini hierarki sebelumnya. Pencocok sangat ampuh dan akan familier bagi mereka yang pernah menggunakan mereka dengan Mockito atau JUnit. Jika Anda tidak terbiasa dengan hamcrest matcher, kami sarankan agar Anda mulai dengan melihat sekilas presentasi.

Sering kali tampilan yang diinginkan memiliki R.id yang unik dan pencocok withId sederhana akan mempersempit pencarian tampilan. Namun, ada banyak kasus yang sah ketika Anda tidak dapat menentukan R.id pada waktu pengembangan pengujian. Misalnya, tampilan spesifik mungkin tidak memiliki R.id atau R.id tidak unik. Hal ini dapat normal uji instrumentasi 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 dikenal dan buka kontennya untuk tampilan tertentu.

Espresso menangani masalah ini secara rapi 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. Ketika ini terjadi, mencoba menggunakan R.id tertentu akan memberi Anda pengecualian, seperti AmbiguousViewMatcherException. Pesan pengecualian memberi Anda teks dari hierarki tampilan saat ini, yang bisa Anda cari 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 melalui berbagai atribut tampilan, Anda mungkin menemukan secara unik properti yang dapat diidentifikasi. Pada contoh di atas, salah satu tampilan memiliki teks "Hello!". Anda dapat menggunakannya untuk mempersempit penelusuran dengan menggunakan kombinasi pencocok:

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 lebih banyak spesifikasi pendukung. Jika Anda tidak dapat mempersempit penelusuran menggunakan withText() atau withContentDescription(), pertimbangkan untuk memperlakukannya sebagai bug aksesibilitas.
  • Gunakan pencocok paling tidak deskriptif yang menemukan satu tampilan yang Anda cari untuk mereka. Jangan terlalu spesifik karena akan memaksa framework untuk melakukan lebih banyak tugas daripada diperlukan. Misalnya, jika tampilan dapat dikenali secara khusus oleh teksnya, Anda tidak perlu menentukan bahwa tampilan juga dapat ditetapkan dari TextView. Untuk banyak tampilan R.id tampilan seharusnya memadai.
  • Jika tampilan target berada di dalam AdapterView—seperti ListView, GridView, atau Spinner—metode onView() mungkin tidak berfungsi. Di kasus, Anda harus menggunakan onData() sebagai gantinya.

Menjalankan tindakan pada tampilan

Setelah menemukan pencocok yang sesuai untuk tampilan target, Anda dapat menjalankan instance ViewAction di dalamnya 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 sedang Anda kerjakan terletak di dalam ScrollView (vertikal atau horizontal), pertimbangkan tindakan sebelumnya yang mengharuskan tampilan ditampilkan—seperti click() dan typeText()—dengan scrollTo(). Ini memastikan bahwa tampilan 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 dipilih saat ini dengan check() . Pernyataan yang paling sering digunakan adalah pernyataan matches(). Proses ini menggunakan ViewMatcher untuk menyatakan status tampilan yang dipilih saat ini.

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!" adalah terlihat—misalnya setelah perubahan tanda visibilitas tampilan— kode Anda sudah cukup.

Pengujian sederhana pernyataan tampilan

Dalam contoh ini, SimpleActivity berisi Button dan TextView. Jika diklik, konten TextView akan berubah menjadi "Hello Espresso!".

Berikut cara mengujinya dengan Espresso:

Klik pada tombol

Langkah pertama adalah mencari properti yang membantu menemukan tombol. Tujuan 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. Sebagai bukan widget statis seperti LinearLayout, hanya sebagian dari Turunan AdapterView dapat dimuat ke dalam hierarki tampilan saat ini. Cara sederhana Penelusuran onView() tidak akan menemukan tampilan yang saat ini tidak dimuat.

Espresso menangani hal ini dengan menyediakan titik entri onData() terpisah yang dapat memuat item adaptor yang dimaksud terlebih dahulu, menjadikannya fokus sebelum yang beroperasi di dalamnya atau anak-anaknya.

Peringatan: Penerapan kustom dari AdapterView dapat mengalami masalah dengan onData() jika mereka memutus kontrak pewarisan, terutama metode API getItem(). Dalam kasus seperti itu, tindakan terbaik adalah memfaktorkan ulang kode aplikasi. Jika tidak dapat melakukannya, Anda dapat mengimplementasikan cocok dengan AdapterViewProtocol kustom. Untuk informasi selengkapnya, ikuti lihat defaultnya Class AdapterViewProtocols 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 dalam kopi. Jika item dipilih, ada TextView yang berubah menjadi "One %s a day!", dengan %s mewakili item yang dipilih.

Tujuan pengujian ini adalah untuk membuka Spinner, memilih item tertentu, dan verifikasi bahwa TextView berisi item tersebut. Karena class Spinner didasarkan diAdapterView, sebaiknya gunakan onData() dan bukan onView() untuk cocok dengan 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 pada tampilan hierarki sebelumnya. Dengan menggunakan onData(), kita memaksa elemen yang diinginkan ke tampilan hierarki sebelumnya. 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. Misalnya:

ViewInteraction: Performing 'single click' action on view with text: Espresso

Hierarki tampilan

Espresso mencetak hierarki tampilan dalam pesan pengecualian saat onView() gagal.

  • Jika onView() tidak menemukan tampilan target, berarti 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 ditampilkan. Hierarki tampilan dicetak dan semua tampilan yang cocok ditandai dengan label MATCHES:
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 akan selalu bermanfaat bila Anda menggunakan Hierarchy Viewer di Android Studio untuk penjelasan.

Peringatan tampilan adapter

Espresso memperingatkan pengguna tentang keberadaan widget AdapterView. Jika 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 informasi selengkapnya tentang menggunakan Espresso dalam pengujian Android, lihat referensi berikut.

Contoh