Tujuan
Pada codelab kali ini Anda akan menguji antar muka dari aplikasi menggunakan Espresso. Tujuannya agar ui dan ux aplikasi dapat berjalan dengan benar. Hasil dari codelab kali ini akan menjadi seperti ini:Skenario Pengujian
Terdapat 4 skenario pengujian Instrumentation Testing:- Pengujian keliling balok.
- Aplikasi terbuka dan menampilkan beberapa view.
- Memberi tindakan input pada edt_lenght, edt_width dan edt_height.
- Memastikan Button btn_save telah ditampilkan.
- Memberi tindakan klik pada btn_save.
- Memastikan Button btn_calculate_circuference telah ditampilkan.
- Memberi tindakan klik pada btn_calculate_circuference.
- Memastikan TextView tv_result telah ditampilkan.
- Memastikan hasil yang tampil sesuai ekspektasi.
- Pengujian volume balok.
- Aplikasi terbuka dan menampilkan beberapa view.
- Memberi tindakan input pada edt_lenght, edt_width dan edt_height.
- Memastikan Button btn_save telah ditampilkan.
- Memberi tindakan klik pada btn_save.
- Memastikan Button btn_calculate_volume telah ditampilkan.
- Memberi tindakan klik pada btn_calculate_volume.
- Memastikan TextView tv_result telah ditampilkan.
- Memastikan hasil yang ditampilkan sesuai dengan ekspektasi.
- Pengujian luas permukaan balok.
- Memberi tindakan input pada edt_lenght, edt_width dan edt_height.
- Memastikan Button btn_save telah ditampilkan.
- Memberi tindakan klik pada btn_save.
- Memastikan Button btn_calculate_surface_area telah ditampilkan.
- Memberi tindakan klik pada btn_calculate_surface_area.
- Memastikan TextView tv_result telah ditampilkan.
- Memastikan hasil yang tampil sesuai ekspektasi.
- Pengujian input panjang, lebar dan tinggi balok.
- Aplikasi terbuka dan menampilkan beberapa view.
- Memberi tindakan empty input pada edt_lenght.
- Memastikan Button btn_save telah ditampilkan.
- Memberi tindakan klik pada btn_save.
- Memastikan eror yang tampil sesuai ekspektasi.
- Memberi tindakan input pada edt_lenght.
- Memberi tindakan empty input pada edt_width.
- Memastikan Button btn_save telah ditampilkan.
- Memberi tindakan klik pada btn_save.
- Memastikan eror yang tampil sesuai ekspektasi.
- Memberi tindakan input pada edt_width.
- Memberi tindakan empty input pada edt_height.
- Memastikan Button btn_save telah ditampilkan.
- Memberi tindakan klik pada btn_save.
- Memastikan eror yang tampil sesuai ekspektasi.
- Memberi tindakan input pada edt_height.
- Memastikan Button btn_save telah ditampilkan.
- Memberi tindakan klik pada btn_save.
Codelab UI Testing
- Bukalah proyek sebelumnya tentang Unit Testing : Latihan Menggunakan Mockito.
- Tentu pertama kali Anda perlu menambahkan library dari Espresso di build.gradle:
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - Untuk membuat proses pengujian berjalan lancar, silakan matikan semua fungsi animasi yang terdapat pada halaman Developer Options pada Setting → Developer Options seperti berikut:
Lakukan untuk semua konfigurasi animasi di atas. Alasannya adalah untuk memudahkan Espresso dalam menjalankan kode dengan beragam resource yang digunakannya. - Sekarang saatnya Anda membuat kelas pengujian otomatis dengan cara buat kelas baru bernama MainActivityEspressoTest pada package utama yang berlabel (androidTest). Caranya, masuklah ke MainActivity kemudian tekan Alt+Enter maka akan muncul tampilan seperti ini:
Pilihlah Create Test, sehingga akan muncul tampilan seperti ini:
Biarkan default seperti tampilan di atas, kemudian pilih OK. Maka akan ada dua pilihan androidTest atau test, untuk Instrumental Test pilihlah androidTest.
Selanjutnya akan terbentuk sebuah kelas seperti ini:
Anda bisa menghapus kelas ExampleInstrumentedTest dan ExampleUnitTest karena kelas tersebut tidak dibutuhkan dalam proyek Anda. - Pertama Anda buka terlebih dahulu berkas MainActivityEspressoTest dan tambahkanlah kode-kode berikut:
Kotlin @RunWith(AndroidJUnit4ClassRunner::class)
class MainActivityTest {
private val dummyVolume = "504.0"
private val dummyCircumference = "100.0"
private val dummySurfaceArea = "396.0"
private val dummyLength = "12.0"
private val dummyWidth = "7.0"
private val dummyHeight = "6.0"
private val emptyInput = ""
private val fieldEmpty = "Field ini tidak boleh kosong"
@get:Rule
var mActivityRule = ActivityTestRule(MainActivity::class.java)
}Java @RunWith(AndroidJUnit4ClassRunner.class)
public class MainActivityTest {
private final String dummyVolume = "504.0";
private final String dummyCircumference = "100.0";
private final String dummySurfaceArea = "396.0";
private final String dummyLength = "12.0";
private final String dummyWidth = "7.0";
private final String dummyHeight = "6.0";
private final String emptyInput = "";
private final String fieldEmpty = "Field ini tidak boleh kosong";
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
} - Setelah itu, Anda akan melakukan pengujian keliling balok seperti yang ada di skenario di atas.
Beberapa metode tidak akan ditawarkan auto-complete-nya oleh Android Studio. Untuk mengatasi ini, Anda dapat menekan Alt + Enter pada beberapa metode-metode tadi dan melakukan static import seperti contoh di bawah ini:Kotlin @RunWith(AndroidJUnit4ClassRunner::class)
class MainActivityTest {
...
@Test
fun assertGetCircumference() {
onView(withId(R.id.edt_length)).perform(typeText(dummyLength), closeSoftKeyboard())
onView(withId(R.id.edt_width)).perform(typeText(dummyWidth), closeSoftKeyboard())
onView(withId(R.id.edt_height)).perform(typeText(dummyHeight), closeSoftKeyboard())
onView(withId(R.id.btn_save)).check(matches(isDisplayed()))
onView(withId(R.id.btn_save)).perform(click())
onView(withId(R.id.btn_calculate_circumference)).check(matches(isDisplayed()))
onView(withId(R.id.btn_calculate_circumference)).perform(click())
onView(withId(R.id.tv_result)).check(matches(isDisplayed()))
onView(withId(R.id.tv_result)).check(matches(withText(dummyCircumference)))
}
}Java @RunWith(AndroidJUnit4ClassRunner.class)
public class MainActivityTest {
...
@Test
public void assertGetCircumference() {
onView(withId(R.id.edt_length)).perform(typeText(dummyLength), closeSoftKeyboard());
onView(withId(R.id.edt_width)).perform(typeText(dummyWidth), closeSoftKeyboard());
onView(withId(R.id.edt_height)).perform(typeText(dummyHeight), closeSoftKeyboard());
onView(withId(R.id.btn_save)).check(matches(isDisplayed()));
onView(withId(R.id.btn_save)).perform(click());
onView(withId(R.id.btn_calculate_circumference)).check(matches(isDisplayed()));
onView(withId(R.id.btn_calculate_circumference)).perform(click());
onView(withId(R.id.tv_result)).check(matches(isDisplayed()));
onView(withId(R.id.tv_result)).check(matches(withText(dummyCircumference)));
}
} - Setelah selesai, sekarang saatnya menjalankan kode pengujian. Jika sebelumnya Anda menjalankan aplikasi terlebih dahulu, maka sekarang pendekatannya dibalik. Anda harus menjalankan kode testnya terlebih dahulu. Caranya, klik kanan pada MainActivityTest dan pilih run ‘MainActivity…’ seperti pada gambar di bawah ini:
Lalu pilih peranti yang terhubung dan akan menjadi tempat pengujian aplikasi.
Perhatikan peranti Anda selama pengujian berlangsung.
Beragam nilai inputan akan dimasukkan secara otomatis. Keren kan? - Setelah berjalan, perhatikan monitor panel di bagian bawah Android Studio yang aktif. Panel tersebut akan berisi informasi progress pengujian dan jika semua pengujian yang dilakukan berhasil maka hasil sukses pada layar seperti di bawah ini akan tampil:
- Selanjutnya tambahkan kode untuk melakukan pengujian menghitung volume, luas permukaan dan input kosong di dalam aplikasi.
Kotlin @RunWith(AndroidJUnit4ClassRunner::class)
class MainActivityTest {
...
@Test
fun assertGetSurfaceArea() {
onView(withId(R.id.edt_length)).perform(typeText(dummyLength), closeSoftKeyboard())
onView(withId(R.id.edt_width)).perform(typeText(dummyWidth), closeSoftKeyboard())
onView(withId(R.id.edt_height)).perform(typeText(dummyHeight), closeSoftKeyboard())
onView(withId(R.id.btn_save)).check(matches(isDisplayed()))
onView(withId(R.id.btn_save)).perform(click())
onView(withId(R.id.btn_calculate_surface_area)).check(matches(isDisplayed()))
onView(withId(R.id.btn_calculate_surface_area)).perform(click())
onView(withId(R.id.tv_result)).check(matches(isDisplayed()))
onView(withId(R.id.tv_result)).check(matches(withText(dummySurfaceArea)))
}
@Test
fun assertGetVolume() {
onView(withId(R.id.edt_length)).perform(typeText(dummyLength), closeSoftKeyboard())
onView(withId(R.id.edt_width)).perform(typeText(dummyWidth), closeSoftKeyboard())
onView(withId(R.id.edt_height)).perform(typeText(dummyHeight), closeSoftKeyboard())
onView(withId(R.id.btn_save)).check(matches(isDisplayed()))
onView(withId(R.id.btn_save)).perform(click())
onView(withId(R.id.btn_calculate_volume)).check(matches(isDisplayed()))
onView(withId(R.id.btn_calculate_volume)).perform(click())
onView(withId(R.id.tv_result)).check(matches(isDisplayed()))
onView(withId(R.id.tv_result)).check(matches(withText(dummyVolume)))
}
//Pengecekan untuk empty input
@Test
fun assertEmptyInput() {
// pengecekan input untuk length
onView(withId(R.id.edt_length)).perform(typeText(emptyInput), closeSoftKeyboard())
onView(withId(R.id.btn_save)).check(matches(isDisplayed()))
onView(withId(R.id.btn_save)).perform(click())
onView(withId(R.id.edt_length)).check(matches(hasErrorText(fieldEmpty)))
onView(withId(R.id.edt_length)).perform(typeText(dummyLength), closeSoftKeyboard())
// pengecekan input untuk width
onView(withId(R.id.edt_width)).perform(typeText(emptyInput), closeSoftKeyboard())
onView(withId(R.id.btn_save)).check(matches(isDisplayed()))
onView(withId(R.id.btn_save)).perform(click())
onView(withId(R.id.edt_width)).check(matches(hasErrorText(fieldEmpty)))
onView(withId(R.id.edt_width)).perform(typeText(dummyWidth), closeSoftKeyboard())
// pengecekan input untuk height
onView(withId(R.id.edt_height)).perform(typeText(emptyInput), closeSoftKeyboard())
onView(withId(R.id.btn_save)).check(matches(isDisplayed()))
onView(withId(R.id.btn_save)).perform(click())
onView(withId(R.id.edt_height)).check(matches(hasErrorText(fieldEmpty)))
onView(withId(R.id.edt_height)).perform(typeText(dummyHeight), closeSoftKeyboard())
onView(withId(R.id.btn_save)).check(matches(isDisplayed()))
onView(withId(R.id.btn_save)).perform(click())
}
}Java @RunWith(AndroidJUnit4ClassRunner.class)
public class MainActivityTest {
...
@Test
public void assertGetSurfaceArea() {
onView(withId(R.id.edt_length)).perform(typeText(dummyLength), closeSoftKeyboard());
onView(withId(R.id.edt_width)).perform(typeText(dummyWidth), closeSoftKeyboard());
onView(withId(R.id.edt_height)).perform(typeText(dummyHeight), closeSoftKeyboard());
onView(withId(R.id.btn_save)).check(matches(isDisplayed()));
onView(withId(R.id.btn_save)).perform(click());
onView(withId(R.id.btn_calculate_surface_area)).check(matches(isDisplayed()));
onView(withId(R.id.btn_calculate_surface_area)).perform(click());
onView(withId(R.id.tv_result)).check(matches(isDisplayed()));
onView(withId(R.id.tv_result)).check(matches(withText(dummySurfaceArea)));
}
@Test
public void assertGetVolume() {
onView(withId(R.id.edt_length)).perform(typeText(dummyLength), closeSoftKeyboard());
onView(withId(R.id.edt_width)).perform(typeText(dummyWidth), closeSoftKeyboard());
onView(withId(R.id.edt_height)).perform(typeText(dummyHeight), closeSoftKeyboard());
onView(withId(R.id.btn_save)).check(matches(isDisplayed()));
onView(withId(R.id.btn_save)).perform(click());
onView(withId(R.id.btn_calculate_volume)).check(matches(isDisplayed()));
onView(withId(R.id.btn_calculate_volume)).perform(click());
onView(withId(R.id.tv_result)).check(matches(isDisplayed()));
onView(withId(R.id.tv_result)).check(matches(withText(dummyVolume)));
}
//Pengecekan untuk empty input
@Test
public void assertEmptyInput() {
// pengecekan input untuk length
onView(withId(R.id.edt_length)).perform(typeText(emptyInput), closeSoftKeyboard());
onView(withId(R.id.btn_save)).check(matches(isDisplayed()));
onView(withId(R.id.btn_save)).perform(click());
onView(withId(R.id.edt_length)).check(matches(hasErrorText(fieldEmpty)));
onView(withId(R.id.edt_length)).perform(typeText(dummyLength), closeSoftKeyboard());
// pengecekan input untuk width
onView(withId(R.id.edt_width)).perform(typeText(emptyInput), closeSoftKeyboard());
onView(withId(R.id.btn_save)).check(matches(isDisplayed()));
onView(withId(R.id.btn_save)).perform(click());
onView(withId(R.id.edt_width)).check(matches(hasErrorText(fieldEmpty)));
onView(withId(R.id.edt_width)).perform(typeText(dummyWidth), closeSoftKeyboard());
// pengecekan input untuk height
onView(withId(R.id.edt_height)).perform(typeText(emptyInput), closeSoftKeyboard());
onView(withId(R.id.btn_save)).check(matches(isDisplayed()));
onView(withId(R.id.btn_save)).perform(click());
onView(withId(R.id.edt_height)).check(matches(hasErrorText(fieldEmpty)));
onView(withId(R.id.edt_height)).perform(typeText(dummyHeight), closeSoftKeyboard());
onView(withId(R.id.btn_save)).check(matches(isDisplayed()));
onView(withId(R.id.btn_save)).perform(click());
}
} - Jalankan kembali instrumental testing Anda, maka hasilnya akan jadi seperti ini:
4 dari 4 skenario pengujian berhasil dilakukan. Luar biasa! - Sekarang Anda sudah berhasil melakukan proses pengujian secara otomatis. Mari kita ulik lebih lanjut. Mari hilangkan baris ini pada kode di MainActivity.
Coba jalankan kode test seperti cara sebelumnya. Seharusnya proses pengujian akan gagal. Anda dapat mengetahui pesan dan penyebab kegagalan pada monitor panel seperti pada gambar di bawah ini:Kotlin edtWidth = findViewById(R.id.edt_width)
Java edtWidth = findViewById(R.id.edt_width);
android.support.test.espresso.PerformException: Error performing 'single click - At Coordinates: 539, 947 and precision: 16, 16' on view 'Animations or transitions are enabled on the target device.
Bagaimana? Masih pusing? Daripada pusing, mari kita bedah kode.
For more info check: http://goo.gl/qVu1yV
with id: com.dicoding.picodiploma.myunittest:id/btn_save'.
at android.support.test.espresso.PerformException$Builder.build(PerformException.java:82)
...
at android.support.test.espresso.ViewInteraction.perform(ViewInteraction.java:114)
at com.dicoding.picodiploma.myunittest.MainActivityTest.assertSurfaceArea(MainActivityTest.java:58)
at java.lang.reflect.Method.invoke(Native Method)
...
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2145)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.text.Editable android.widget.EditText.getText()' on a null object reference
at com.dicoding.picodiploma.myunittest.MainActivity.onClick(MainActivity.java:46)
at android.view.View.performClick(View.java:6597)
Bedah Kode
Espresso
Sebelumnya, kita perlu ingat kembali tiga komponen utama dari Espresso:- ViewMatchers (onView(ViewMatcher)), untuk menemukan elemen atau komponen antar muka yang diuji.
- ViewActions (perform(ViewAction)), untuk memberikan event untuk melakukan sebuah aksi pada komponen antar muka yang diuji.
- ViewAssertions(check(ViewAssertion)), untuk mengecek sebuah kondisi atau state dari komponen yang diuji.
Kotlin |
@get:Rule |
Java |
@Rule |
Selanjutnya perhatikan kode berikut:
Kotlin |
onView(withId(R.id.edt_length)).perform(typeText(dummyLength), closeSoftKeyboard()) |
Java |
onView(withId(R.id.edt_length)).perform(typeText(dummyLength), closeSoftKeyboard()); |
Selanjutnya kode berikut:
Kotlin |
onView(withId(R.id.btn_save)).check(matches(isDisplayed())) |
Java |
onView(withId(R.id.btn_save)).check(matches(isDisplayed())); |
Lalu perhatikan kode berikut:
Kotlin |
onView(withId(R.id.btn_save)).perform(click()) |
Java |
onView(withId(R.id.btn_save)).perform(click()); |
Dan yang paling penting adalah ini:
Kotlin |
onView(withId(R.id.tv_result)).check(matches(withText(dummyCircumference))) |
Java |
onView(withId(R.id.tv_result)).check(matches(withText(dummyCircumference))); |
Perhatikan juga kode berikut yang berfungsi untuk mengecek eror pada EditText:
Kotlin |
onView(withId(R.id.edt_length)).check(matches(hasErrorText(fieldEmpty))) |
Java |
onView(withId(R.id.edt_length)).check(matches(hasErrorText(fieldEmpty))); |
Bagaimana? Mudah kan proses pengujian antarmuka dengan menggunakan Espresso? Tak hanya mudah tapi juga efisien jika Anda berhadapan dengan kasus yang lebih besar. Kebiasaan menuliskan kode pengujian akan melatih Anda lebih kritis terhadap beragam kondisi yang akan terjadi pada tiap fungsi di dalam aplikasi.
Apalagi bila Anda menulis kode test sebelum kode aplikasi. Proses pengembangan aplikasi Anda dijamin jauh lebih baik dan mampu menghasilkan produk yang lebih berkualitas! Proses pengujian otomatis di atas masih sangatlah sederhana. Kami tantang Anda untuk mengeksplorasi lebih jauh kemampuan Espresso lainnya!
Seperti biasa, agar Anda lebih paham tentang materi ini, kami sarankan Anda membaca tiap tautan di bawah ini:
Untuk source code materi ini silakan Anda unduh di: