Menguji interaksi pengguna dalam satu aplikasi membantu memastikan bahwa pengguna tidak mendapatkan hasil yang tidak terduga atau memiliki pengalaman buruk saat berinteraksi dengan aplikasi Anda. Anda harus mempelajari cara membuat pengujian antarmuka pengguna (UI) jika Anda perlu memverifikasi bahwa UI aplikasi Anda berfungsi dengan benar.
Framework pengujian Espresso, yang disediakan oleh AndroidX Test, menyediakan API untuk menulis pengujian UI guna menyimulasikan interaksi pengguna dalam satu aplikasi target. Pengujian Espresso dapat dijalankan di perangkat yang menjalankan Android 2.3.3 (API level 10) dan yang lebih tinggi. Manfaat utama menggunakan Espresso adalah framework pengujian ini menyediakan sinkronisasi tindakan pengujian secara otomatis dengan UI aplikasi yang sedang Anda uji.
Espresso mendeteksi kapan thread utama tidak digunakan, sehingga dapat menjalankan perintah pengujian Anda pada waktu yang tepat, yang akan meningkatkan keandalan pengujian. Kemampuan ini juga membebaskan Anda dari keharusan untuk menambah solusi pengaturan waktu, seperti Thread.sleep()
dalam kode pengujian Anda.
Framework pengujian Espresso adalah API berbasis instrumentasi dan bekerja dengan runner pengujian AndroidJUnitRunner
.
Menyiapkan Espresso
Sebelum membuat pengujian UI Anda dengan Espresso, pastikan untuk menetapkan referensi dependensi ke library Espresso:
dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' }
Nonaktifkan animasi pada perangkat pengujian Anda. Membiarkan animasi sistem tetap aktif pada perangkat pengujian dapat menyebabkan hasil yang tidak terduga atau dapat menyebabkan pengujian Anda gagal. Nonaktifkan animasi dari Setelan dengan membuka Opsi developer dan nonaktifkan semua opsi berikut:
- Skala animasi jendela
- Skala animasi transisi
- Skala durasi animator
Jika Anda ingin menyiapkan project untuk menggunakan fitur Espresso selain dari fitur yang disediakan core API, lihat panduan khusus untuk Espresso.
Membuat class pengujian Espresso
Untuk membuat pengujian Espresso, ikuti model pemrograman ini:
- Temukan komponen UI yang ingin Anda uji dalam
Activity
(misalnya, tombol login di aplikasi) dengan memanggil metodeonView()
, atau metodeonData()
untuk kontrolAdapterView
. -
Simulasikan interaksi pengguna tertentu untuk dijalankan pada komponen UI tersebut, dengan memanggil metode
ViewInteraction.perform()
atauDataInteraction.perform()
dan meneruskan tindakan pengguna (misalnya, klik pada tombol login). Untuk mengurutkan beberapa tindakan pada komponen UI yang sama, gabungkan tindakan tersebut dengan menggunakan daftar yang dipisahkan koma dalam argumen metode Anda. - Bila perlu ulangi langkah-langkah di atas, untuk mensimulasikan alur penggunaan di beberapa aktivitas di aplikasi target.
- Gunakan metode
ViewAssertions
untuk memeriksa apakah UI mencerminkan keadaan atau perilaku yang diharapkan setelah interaksi pengguna ini dilakukan.
Langkah ini dibahas lebih detail di bagian di bawah ini.
Cuplikan kode berikut ini menunjukkan bagaimana class pengujian Anda dapat memanggil alur kerja dasar ini:
Kotlin
onView(withId(R.id.my_view)) // withId(R.id.my_view) is a ViewMatcher .perform(click()) // click() is a ViewAction .check(matches(isDisplayed())) // matches(isDisplayed()) is a ViewAssertion
Java
onView(withId(R.id.my_view)) // withId(R.id.my_view) is a ViewMatcher .perform(click()) // click() is a ViewAction .check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion
Menggunakan Espresso dengan ActivityTestRule
Bagian berikut menjelaskan cara membuat pengujian Espresso baru dalam gaya JUnit 4 dan menggunakan ActivityTestRule
untuk mengurangi jumlah kode boilerplate yang perlu Anda tulis. Dengan menggunakan ActivityTestRule
, framework pengujian menjalankan aktivitas yang sedang diuji sebelum setiap metode pengujian yang dianotasikan dengan @Test
dan sebelum metode apa pun dianotasi dengan @Before
. Framework ini menangani penonaktifan aktivitas setelah pengujian selesai dan semua metode yang dianotasikan dengan @After
dijalankan.
Kotlin
package com.example.android.testing.espresso.BasicSample import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import androidx.test.rule.ActivityTestRule import androidx.test.runner.AndroidJUnit4 @RunWith(AndroidJUnit4::class) @LargeTest class ChangeTextBehaviorTest { private lateinit var stringToBetyped: String @get:Rule var activityRule: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java) @Before fun initValidString() { // Specify a valid string. stringToBetyped = "Espresso" } @Test fun changeText_sameActivity() { // Type text and then press the button. onView(withId(R.id.editTextUserInput)) .perform(typeText(stringToBetyped), closeSoftKeyboard()) onView(withId(R.id.changeTextBt)).perform(click()) // Check that the text was changed. onView(withId(R.id.textToBeChanged)) .check(matches(withText(stringToBetyped))) } }
Java
package com.example.android.testing.espresso.BasicSample; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; @RunWith(AndroidJUnit4.class) @LargeTest public class ChangeTextBehaviorTest { private String stringToBetyped; @Rule public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class); @Before public void initValidString() { // Specify a valid string. stringToBetyped = "Espresso"; } @Test public void changeText_sameActivity() { // Type text and then press the button. onView(withId(R.id.editTextUserInput)) .perform(typeText(stringToBetyped), closeSoftKeyboard()); onView(withId(R.id.changeTextBt)).perform(click()); // Check that the text was changed. onView(withId(R.id.textToBeChanged)) .check(matches(withText(stringToBetyped))); } }
Mengakses komponen UI
Sebelum Espresso dapat berinteraksi dengan aplikasi yang diuji, Anda harus terlebih dahulu menentukan komponen atau tampilan UI. Espresso mendukung penggunaan Hamcrest matcher untuk menentukan tampilan dan adaptor di aplikasi Anda.
Untuk menemukan tampilan, panggil metode onView()
dan teruskan matcher tampilan yang menentukan tampilan yang Anda targetkan. Tindakan ini dijelaskan lebih mendetail di Menentukan matcher tampilan.
Metode onView()
menampilkan objek ViewInteraction
yang memungkinkan pengujian Anda berinteraksi dengan tampilan.
Namun, memanggil metode onView()
mungkin tidak berfungsi jika Anda ingin mencari tampilan dalam tata letak RecyclerView
.
Dalam hal ini, ikuti instruksi diMenemukan lokasi di AdapterView sebagai gantinya.
Catatan: Metode onView()
tidak memeriksa apakah tampilan yang Anda tentukan valid. Sebagai gantinya, Espresso hanya mencari hierarki tampilan saat ini menggunakan pencocokan yang disediakan.
Jika tidak ada kecocokan yang ditemukan, metode akan menampilkan NoMatchingViewException
.
Cuplikan kode berikut ini menunjukkan bagaimana Anda dapat menulis pengujian yang mengakses kolom EditText
, memasukkan string teks, menutup keyboard virtual, lalu mengklik tombol.
Kotlin
fun testChangeText_sameActivity() { // Type text and then press the button. onView(withId(R.id.editTextUserInput)) .perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard()) onView(withId(R.id.changeTextButton)).perform(click()) // Check that the text was changed. ... }
Java
public void testChangeText_sameActivity() { // Type text and then press the button. onView(withId(R.id.editTextUserInput)) .perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard()); onView(withId(R.id.changeTextButton)).perform(click()); // Check that the text was changed. ... }
Menentukan matcher tampilan
Anda dapat menentukan matcher tampilan dengan menggunakan pendekatan ini:
- Metode pemanggilan di class
ViewMatchers
. Misalnya, untuk menemukan tampilan dengan mencari string teks yang ditampilkan oleh tampilan, Anda dapat memanggil metode seperti ini:Kotlin
onView(withText("Sign-in"))
Java
onView(withText("Sign-in"));
Demikian pula, Anda dapat memanggil
withId()
dan memberikan resource ID (R.id
) dari tampilan, seperti yang ditunjukkan pada contoh berikut:Kotlin
onView(withId(R.id.button_signin))
Java
onView(withId(R.id.button_signin));
Tidak ada jaminan bahwa ID resource Android akan bersifat unik. Jika pengujian Anda mencoba mencocokkan dengan ID resource yang digunakan oleh lebih dari satu tampilan, Espresso akan menampilkan
AmbiguousViewMatcherException
. -
Menggunakan class
Matchers
Hamcrest. Anda dapat menggunakan metodeallOf()
untuk menggabungkan beberapa matcher, seperticontainsString()
daninstanceOf()
. Pendekatan ini memungkinkan Anda untuk memfilter hasil kecocokan dengan lebih sempit, seperti yang ditunjukkan pada contoh berikut:Kotlin
onView(allOf(withId(R.id.button_signin), withText("Sign-in")))
Java
onView(allOf(withId(R.id.button_signin), withText("Sign-in")));
Anda dapat menggunakan kata kunci
not
untuk memfilter tampilan yang tidak sesuai dengan matcher, seperti yang ditunjukkan pada contoh berikut:Kotlin
onView(allOf(withId(R.id.button_signin), not(withText("Sign-out"))))
Java
onView(allOf(withId(R.id.button_signin), not(withText("Sign-out"))));
Untuk menggunakan metode ini dalam pengujian Anda, impor paket
org.hamcrest.Matchers
. Untuk mempelajari pencocokan Hamcrest lebih lanjut, lihat situs Hamcrest.
Untuk meningkatkan performa pengujian Espresso Anda, tentukan informasi pencocokan minimum yang diperlukan untuk menemukan tampilan target Anda. Misalnya, jika tampilan secara unik dapat diidentifikasi oleh teks deskriptif, Anda tidak perlu menentukan bahwa tampilan juga dapat ditetapkan dari instance TextView
.
Menemukan tampilan di AdapterView
Pada widget AdapterView
, tampilan akan diisi secara dinamis dengan tampilan turunan saat waktu proses. Jika tampilan target yang ingin Anda uji berada di dalam AdapterView
(seperti ListView
, GridView
, atau Spinner
), metode onView()
mungkin tidak berfungsi karena hanya subset tampilan yang dapat dimuat dalam hierarki tampilan saat ini.
Sebagai gantinya, panggil metode onData()
untuk mendapatkan objek DataInteraction
guna mengakses elemen tampilan target.
Espresso mengelola pemuatan elemen tampilan target ke dalam hierarki tampilan saat ini. Espresso juga mengelola scroll ke elemen target dan memfokuskan elemen.
Catatan: Metode onData()
tidak memeriksa apakah item yang Anda tentukan sesuai dengan tampilan. Espresso hanya menelusuri hierarki tampilan saat ini. Jika tidak ada kecocokan yang ditemukan, metode akan menampilkan NoMatchingViewException
.
Cuplikan kode berikut menunjukkan bagaimana Anda dapat menggunakan metode onData()
bersama dengan pencocokan Hamcrest untuk mencari baris tertentu dalam daftar yang berisi string yang diberikan.
Dalam contoh ini, class LongListActivity
berisi daftar string yang diekspos melalui SimpleAdapter
.
Kotlin
onData(allOf(`is`(instanceOf(Map::class.java)), hasEntry(equalTo(LongListActivity.ROW_TEXT), `is`("test input"))))
Java
onData(allOf(is(instanceOf(Map.class)), hasEntry(equalTo(LongListActivity.ROW_TEXT), is("test input"))));
Melakukan tindakan
Panggil metode ViewInteraction.perform()
atau DataInteraction.perform()
untuk menyimulasi interaksi pengguna pada komponen UI. Anda harus meneruskan satu atau beberapa objek ViewAction
sebagai argumen. Espresso memicu setiap tindakan secara berurutan sesuai dengan urutan yang diberikan, dan menjalankan tindakan tersebut di thread utama.
Class ViewActions
menyediakan daftar metode pembantu untuk menentukan tindakan umum.
Anda dapat menggunakan metode ini sebagai pintasan yang mudah daripada membuat dan mengonfigurasi objek ViewAction
individual. Anda dapat menentukan tindakan seperti:
ViewActions.click()
: Mengklik pada tampilan.ViewActions.typeText()
: Mengklik pada tampilan dan memasukkan string tertentu.-
ViewActions.scrollTo()
: Men-scroll ke tampilan. Tampilan target harus dijadikan subclass dariScrollView
dan nilai propertiandroid:visibility
-nya harusVISIBLE
. Untuk tampilan yang memperluasAdapterView
(misalnya,ListView
), metodeonData()
menangani scroll untuk Anda. ViewActions.pressKey()
: Melakukan aktivitas penekanan tombol menggunakan kode tombol yang ditentukan.ViewActions.clearText()
: Menghapus teks dalam tampilan target.
Jika tampilan target berada di dalam ScrollView
, lakukan tindakan ViewActions.scrollTo()
terlebih dahulu untuk menampilkan tampilan di layar lain sebelum melanjutkan dengan tindakan lain. Tindakan ViewActions.scrollTo()
tidak akan berpengaruh jika tampilan sudah ditampilkan.
Menguji aktivitas Anda secara terpisah dengan Intent Espresso
Intent Espresso memungkinkan validasi dan penghentian intent yang dikirim oleh aplikasi. Dengan Intent Espresso, Anda dapat menguji aplikasi, aktivitas, atau layanan secara terpisah dengan mencegah intent keluar, menghentikan hasilnya, dan mengirimkan hasil kembali ke komponen yang diuji.
Untuk memulai pengujian dengan Intent Espresso, Anda perlu menambahkan baris berikut ke file build.gradle aplikasi Anda:
dependencies { androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0' }
Untuk menguji intent, Anda perlu membuat instance class IntentsTestRule, yang sangat mirip dengan class ActivityTestRule. Class IntentsTestRule menginisialisasi Intent Espresso sebelum setiap pengujian, menghentikan aktivitas host, dan melepaskan Intent Espresso setelah setiap pengujian.
Class pengujian yang ditunjukkan dalam cuplikan kode berikut menyediakan pengujian sederhana untuk intent eksplisit. Pengujian ini menguji aktivitas dan intent yang dibuat dalam tutorial Membuat Aplikasi Pertama Anda.
Kotlin
private const val MESSAGE = "This is a test" private const val PACKAGE_NAME = "com.example.myfirstapp" @RunWith(AndroidJUnit4::class) class SimpleIntentTest { /* Instantiate an IntentsTestRule object. */ @get:Rule var intentsRule: IntentsTestRule<MainActivity> = IntentsTestRule(MainActivity::class.java) @Test fun verifyMessageSentToMessageActivity() { // Types a message into a EditText element. onView(withId(R.id.edit_message)) .perform(typeText(MESSAGE), closeSoftKeyboard()) // Clicks a button to send the message to another // activity through an explicit intent. onView(withId(R.id.send_message)).perform(click()) // Verifies that the DisplayMessageActivity received an intent // with the correct package name and message. intended(allOf( hasComponent(hasShortClassName(".DisplayMessageActivity")), toPackage(PACKAGE_NAME), hasExtra(MainActivity.EXTRA_MESSAGE, MESSAGE))) } }
Java
@Large @RunWith(AndroidJUnit4.class) public class SimpleIntentTest { private static final String MESSAGE = "This is a test"; private static final String PACKAGE_NAME = "com.example.myfirstapp"; /* Instantiate an IntentsTestRule object. */ @Rule public IntentsTestRule<MainActivity> intentsRule = new IntentsTestRule<>(MainActivity.class); @Test public void verifyMessageSentToMessageActivity() { // Types a message into a EditText element. onView(withId(R.id.edit_message)) .perform(typeText(MESSAGE), closeSoftKeyboard()); // Clicks a button to send the message to another // activity through an explicit intent. onView(withId(R.id.send_message)).perform(click()); // Verifies that the DisplayMessageActivity received an intent // with the correct package name and message. intended(allOf( hasComponent(hasShortClassName(".DisplayMessageActivity")), toPackage(PACKAGE_NAME), hasExtra(MainActivity.EXTRA_MESSAGE, MESSAGE))); } }
Untuk mengetahui informasi selengkapnya tentang Intent Espresso, lihat Dokumentasi Intent Espresso di situs AndroidX Test. Anda juga dapat mendownload contoh kode IntentsBasicSample dan IntentsAdvancedSample.
Menguji WebViews dengan Espresso Web
Espresso Web memungkinkan Anda untuk menguji komponen WebView
yang berada dalam suatu aktivitas. Espresso Web menggunakan WebDriver API untuk memeriksa dan mengontrol perilaku WebView
.
Untuk memulai pengujian dengan Espresso Web, Anda perlu menambahkan baris berikut ke file build.gradle aplikasi:
dependencies { androidTestImplementation 'androidx.test.espresso:espresso-web:3.1.0' }
Jika membuat pengujian menggunakan Espresso Web, Anda harus mengaktifkan JavaScript di WebView
saat Anda membuat instance objek ActivityTestRule untuk menguji aktivitas. Pada saat pengujian, Anda dapat memilih elemen HTML yang ditampilkan di WebView
dan mensimulasikan interaksi pengguna, seperti memasukkan teks ke dalam kotak teks lalu mengklik tombol. Setelah tindakan selesai, Anda kemudian dapat memverifikasi bahwa hasil pada halaman Web cocok dengan hasil yang Anda harapkan.
Dalam cuplikan kode berikut, class menguji komponen WebView
dengan nilai id 'webview' dalam aktivitas yang sedang diuji.
Pengujian typeTextInInput_clickButton_SubmitsForm()
memilih elemen <input>
di halaman Web, memasukkan beberapa teks, dan memeriksa teks yang muncul di elemen lain.
Kotlin
private const val MACCHIATO = "Macchiato" private const val DOPPIO = "Doppio" @LargeTest @RunWith(AndroidJUnit4::class) class WebViewActivityTest { @get:Rule val activityRule = object : ActivityTestRule<WebViewActivity>( WebViewActivity::class.java, false, /* Initial touch mode */ false /* launch activity */ ) { override fun afterActivityLaunched() { // Enable JavaScript. onWebView().forceJavascriptEnabled() } } @Test fun typeTextInInput_clickButton_SubmitsForm() { // Lazily launch the Activity with a custom start Intent per test activityRule.launchActivity(withWebFormIntent()) // Selects the WebView in your layout. // If you have multiple WebViews you can also use a // matcher to select a given WebView, onWebView(withId(R.id.web_view)). onWebView() // Find the input element by ID .withElement(findElement(Locator.ID, "text_input")) // Clear previous input .perform(clearElement()) // Enter text into the input element .perform(DriverAtoms.webKeys(MACCHIATO)) // Find the submit button .withElement(findElement(Locator.ID, "submitBtn")) // Simulate a click via JavaScript .perform(webClick()) // Find the response element by ID .withElement(findElement(Locator.ID, "response")) // Verify that the response page contains the entered text .check(webMatches(getText(), containsString(MACCHIATO))) } }
Java
@LargeTest @RunWith(AndroidJUnit4.class) public class WebViewActivityTest { private static final String MACCHIATO = "Macchiato"; private static final String DOPPIO = "Doppio"; @Rule public ActivityTestRule<WebViewActivity> activityRule = new ActivityTestRule<WebViewActivity>(WebViewActivity.class, false /* Initial touch mode */, false /* launch activity */) { @Override protected void afterActivityLaunched() { // Enable JavaScript. onWebView().forceJavascriptEnabled(); } } @Test public void typeTextInInput_clickButton_SubmitsForm() { // Lazily launch the Activity with a custom start Intent per test activityRule.launchActivity(withWebFormIntent()); // Selects the WebView in your layout. // If you have multiple WebViews you can also use a // matcher to select a given WebView, onWebView(withId(R.id.web_view)). onWebView() // Find the input element by ID .withElement(findElement(Locator.ID, "text_input")) // Clear previous input .perform(clearElement()) // Enter text into the input element .perform(DriverAtoms.webKeys(MACCHIATO)) // Find the submit button .withElement(findElement(Locator.ID, "submitBtn")) // Simulate a click via JavaScript .perform(webClick()) // Find the response element by ID .withElement(findElement(Locator.ID, "response")) // Verify that the response page contains the entered text .check(webMatches(getText(), containsString(MACCHIATO))); } }
Untuk informasi selengkapnya tentang Espresso Web, lihat dokumentasi Espresso Web di situs AndroidX Test. Anda juga dapat mendownload cuplikan kode ini sebagai bagian dari contoh kode Espresso Web.
Memverifikasi hasil
Panggil metode ViewInteraction.check()
atau DataInteraction.check()
untuk menyatakan bahwa tampilan di UI cocok dengan beberapa status yang diharapkan. Anda harus meneruskan objek ViewAssertion
sebagai argumen. Jika pernyataan tersebut gagal, Espresso akan menampilkan AssertionFailedError
.
Class ViewAssertions
menyediakan daftar metode helper untuk menentukan pernyataan umum. Pernyataan yang dapat Anda gunakan meliputi:
doesNotExist
: Menyatakan bahwa tidak ada tampilan yang cocok dengan kriteria yang ditentukan dalam hierarki tampilan saat ini.matches
: Menyatakan bahwa tampilan yang ditentukan ada dalam hierarki tampilan saat ini dan statusnya cocok dengan beberapa Hamcrest matcher yang diberikan.selectedDescendentsMatch
: Menyatakan bahwa tampilan turunan yang ditentukan untuk tampilan induk ada, dan statusnya cocok dengan beberapa Hamcrest matcher yang diberikan.
Cuplikan kode berikut menunjukkan cara Anda dapat memeriksa apakah teks yang ditampilkan di UI memiliki nilai yang sama dengan teks yang sebelumnya dimasukkan dalam kolom EditText
.
Kotlin
fun testChangeText_sameActivity() { // Type text and then press the button. ... // Check that the text was changed. onView(withId(R.id.textToBeChanged)) .check(matches(withText(STRING_TO_BE_TYPED))) }
Java
public void testChangeText_sameActivity() { // Type text and then press the button. ... // Check that the text was changed. onView(withId(R.id.textToBeChanged)) .check(matches(withText(STRING_TO_BE_TYPED))); }
Menjalankan pengujian Espresso pada perangkat atau emulator
Anda dapat menjalankan pengujian Espresso dari Android Studio atau dari command line. Pastikan untuk menentukan AndroidJUnitRunner
sebagai runner instrumentasi default di project Anda.
Untuk menjalankan pengujian Espresso Anda, ikuti langkah-langkah untuk menjalankan pengujian berinstrumen yang dijelaskan dalam Memulai Pengujian.
Anda juga harus membaca Referensi Espresso API.
Referensi lainnya
Untuk mengetahui informasi selengkapnya tentang penggunaan UI Automator dalam pengujian Android, lihat referensi berikut.
Contoh
- Contoh Kode Espresso mencakup pilihan contoh Espresso lengkap.