Menguji UI untuk beberapa aplikasi

Pengujian antarmuka pengguna (UI) yang melibatkan interaksi pengguna di beberapa aplikasi memungkinkan Anda memverifikasi bahwa aplikasi Anda berperilaku dengan benar saat alur pengguna melewati aplikasi lain atau UI sistem. Contoh alur pengguna tersebut adalah aplikasi pesan yang memungkinkan pengguna memasukkan pesan teks, meluncurkan pemilih kontak Android sehingga pengguna dapat memilih penerima yang akan dikirimi pesan, lalu menampilkan kontrol ke aplikasi asli yang digunakan pengguna untuk mengirim pesan.

Tutorial ini mencakup cara menulis pengujian UI tersebut menggunakan framework pengujian UI Automator yang disediakan oleh AndroidX Test. UI Automator API memungkinkan Anda berinteraksi dengan elemen yang terlihat di perangkat, terlepas dari Activity mana pun yang difokuskan. Pengujian Anda bisa mencari komponen UI dengan menggunakan deskriptor sederhana seperti teks yang ditampilkan dalam komponen itu atau deskripsi kontennya. Pengujian UI Automator bisa dijalankan pada perangkat yang menjalankan Android 4.3 (API level 18) atau yang lebih tinggi.

Framework pengujian UI Automator adalah API berbasis instrumentasi dan bekerja dengan test runner AndroidJUnitRunner.

Anda juga harus membaca Referensi UI Automator API dan mencoba Contoh Kode UI Automator.

Menyiapkan UI Automator

Sebelum membuat pengujian UI dengan UI Automator, pastikan untuk mengonfigurasi lokasi kode sumber pengujian dan dependensi project Anda, seperti yang dijelaskan dalam Menyiapkan project untuk AndroidX Test.

Dalam file build.gradle modul aplikasi Android, Anda harus menentukan referensi dependensi ke library UI Automator:

    dependencies {
        ...
        androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
    }
    

Untuk mengoptimalkan pengujian UI Automator, langkah pertama yang harus Anda lakukan adalah memeriksa komponen UI aplikasi target dan memastikannya dapat diakses. Tips pengoptimalan ini dijelaskan dalam dua bagian berikutnya.

Memeriksa UI pada perangkat

Sebelum mendesain pengujian Anda, periksa komponen UI yang terlihat pada perangkat. Untuk memastikan bahwa pengujian UI Automator Anda dapat mengakses komponen ini, periksa apakah komponen ini memiliki label teks yang terlihat, nilai android:contentDescription, atau keduanya.

Fitur uiautomatorviewer menyediakan antarmuka visual yang nyaman untuk memeriksa hierarki tata letak dan melihat properti komponen UI yang terlihat di latar depan perangkat. Dengan informasi ini, Anda dapat membuat lebih banyak pengujian lebih terperinci menggunakan UI Automator. Misalnya, Anda dapat membuat pemilih UI yang cocok dengan properti terlihat tertentu.

Untuk meluncurkan fitur uiautomatorviewer:

  1. Luncurkan aplikasi target di perangkat fisik.
  2. Hubungkan perangkat ke mesin pengembangan Anda.
  3. Buka jendela terminal dan buka direktori <android-sdk>/tools/.
  4. Jalankan fitur tersebut dengan perintah ini:
    $ uiautomatorviewer

Untuk melihat properti UI untuk aplikasi Anda:

  1. Di antarmuka uiautomatorviewer, klik tombol Device Screenshot.
  2. Arahkan kursor ke panel sebelah kiri snapshot untuk melihat komponen UI yang diidentifikasi oleh fitur uiautomatorviewer. Properti dicantumkan di panel kanan bawah dan hierarki tata letak di panel kanan atas.
  3. (Opsional) Klik tombol Toggle NAF Nodes untuk melihat komponen UI yang tidak dapat diakses oleh UI Automator. Hanya informasi terbatas yang tersedia untuk komponen ini.

Untuk mempelajari jenis umum komponen UI yang disediakan oleh Android, lihat Antarmuka Pengguna.

Memastikan aktivitas Anda dapat diakses

Framework pengujian UI Automator berperforma lebih baik pada aplikasi yang telah menerapkan fitur aksesibilitas Android. Saat menggunakan elemen UI jenis View, atau subclass View dari SDK, Anda tidak perlu menerapkan dukungan aksesibilitas, karena class ini sudah melakukannya untuk Anda.

Namun, beberapa aplikasi menggunakan elemen UI kustom untuk memberikan pengalaman pengguna yang lebih kaya. Elemen tersebut tidak akan memberikan dukungan aksesibilitas secara otomatis. Jika aplikasi Anda berisi instance dari subclass View yang bukan dari SDK, pastikan untuk menambahkan fitur aksesibilitas ke elemen ini dengan melakukan langkah berikut:

  1. Buat class konkret yang memperluas ExploreByTouchHelper.
  2. Kaitkan instance dari class baru Anda dengan elemen UI kustom dengan memanggil setAccessibilityDelegate().

Untuk mendapatkan panduan tambahan terkait menambahkan fitur aksesibilitas ke elemen tampilan kustom, lihat Membuat Tampilan Kustom yang Dapat Diakses. Untuk mempelajari lebih lanjut praktik terbaik yang umum untuk aksesibilitas di Android, lihat Membuat Aplikasi Lebih Mudah Diakses.

Membuat class pengujian UI Automator

Class pengujian UI Automator Anda harus ditulis dengan cara yang sama seperti class pengujian JUnit 4. Untuk mempelajari lebih lanjut cara membuat class pengujian JUnit 4 serta menggunakan pernyataan dan anotasi JUnit 4, lihat Membuat Class Pengujian Unit Berinstrumen.

Tambahkan anotasi @RunWith(AndroidJUnit4.class) di awal definisi class penggujian Anda. Anda juga harus menentukan class AndroidJUnitRunner, yang disediakan di AndroidX Test, sebagai test runner default Anda. Langkah ini dijelaskan secara lebih mendetail dalam Menjalankan pengujian UI Automator pada perangkat atau emulator.

Implementasikan model pemrograman berikut ke class pengujian UI Automator Anda:

  1. Dapatkan objek UiDevice untuk mengakses perangkat yang ingin Anda uji, dengan memanggil metode getInstance() dan meneruskan objek Instrumentation sebagai argumen.
  2. Dapatkan objek UiObject untuk mengakses komponen UI yang ditampilkan pada perangkat (misalnya, tampilan saat ini di latar depan), dengan memanggil metode findObject().
  3. Simulasikan interaksi pengguna tertentu untuk dijalankan pada komponen UI tersebut, dengan memanggil metode UiObject; misalnya, panggil performMultiPointerGesture() untuk menyimulasikan gerakan multi-sentuh, dan setText() untuk mengedit kolom teks. Bila perlu, ulangi langkah 2 dan 3 untuk memanggil API guna menguji interaksi pengguna yang lebih kompleks yang melibatkan beberapa komponen UI atau serangkaian tindakan pengguna.
  4. Periksa apakah UI mencerminkan status atau perilaku yang diharapkan, setelah interaksi pengguna ini dilakukan.

Langkah ini dibahas lebih detail di bagian di bawah ini.

Mengakses komponen UI

Objek UiDevice adalah cara utama untuk mengakses dan memanipulasi status perangkat. Dalam pengujian, Anda bisa memanggil metode UiDevice untuk memeriksa status berbagai properti, misalnya orientasi saat ini atau ukuran tampilan. Pengujian Anda dapat menggunakan objek UiDevice untuk menjalankan tindakan tingkat perangkat, misalnya memaksa perangkat berpindah ke rotasi tertentu, menekan tombol fisik D-pad, dan menekan tombol Menu dan Beranda.

Sebaiknya mulai pengujian Anda dari Layar utama perangkat. Dari Layar utama (atau beberapa lokasi awal lain yang Anda pilih pada perangkat), Anda dapat memanggil metode yang disediakan oleh UI Automator API untuk memilih dan berinteraksi dengan elemen UI tertentu.

Cuplikan kode berikut menunjukkan cara pengujian Anda mendapatkan instance UiDevice dan menyimulasikan penekanan Tombol layar utama:

Kotlin

    import org.junit.Before
    import androidx.test.runner.AndroidJUnit4
    import androidx.test.uiautomator.UiDevice
    import androidx.test.uiautomator.By
    import androidx.test.uiautomator.Until
    ...

    private const val BASIC_SAMPLE_PACKAGE = "com.example.android.testing.uiautomator.BasicSample"
    private const val LAUNCH_TIMEOUT = 5000L
    private const val STRING_TO_BE_TYPED = "UiAutomator"

    @RunWith(AndroidJUnit4::class)
    @SdkSuppress(minSdkVersion = 18)
    class ChangeTextBehaviorTest2 {

        private lateinit var device: UiDevice

        @Before
        fun startMainActivityFromHomeScreen() {
            // Initialize UiDevice instance
            device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())

            // Start from the home screen
            device.pressHome()

            // Wait for launcher
            val launcherPackage: String = device.launcherPackageName
            assertThat(launcherPackage, notNullValue())
            device.wait(
                    Until.hasObject(By.pkg(launcherPackage).depth(0)),
                    LAUNCH_TIMEOUT
            )

            // Launch the app
            val context = ApplicationProvider.getApplicationContext<Context>()
            val intent = context.packageManager.getLaunchIntentForPackage(
                    BASIC_SAMPLE_PACKAGE).apply {
                // Clear out any previous instances
                addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
            }
            context.startActivity(intent)

            // Wait for the app to appear
            device.wait(
                    Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)),
                    LAUNCH_TIMEOUT
            )
        }

    }
    

Java

    import org.junit.Before;
    import androidx.test.runner.AndroidJUnit4;
    import androidx.test.uiautomator.UiDevice;
    import androidx.test.uiautomator.By;
    import androidx.test.uiautomator.Until;
    ...

    @RunWith(AndroidJUnit4.class)
    @SdkSuppress(minSdkVersion = 18)
    public class ChangeTextBehaviorTest {

        private static final String BASIC_SAMPLE_PACKAGE
                = "com.example.android.testing.uiautomator.BasicSample";
        private static final int LAUNCH_TIMEOUT = 5000;
        private static final String STRING_TO_BE_TYPED = "UiAutomator";
        private UiDevice device;

        @Before
        public void startMainActivityFromHomeScreen() {
            // Initialize UiDevice instance
            device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());

            // Start from the home screen
            device.pressHome();

            // Wait for launcher
            final String launcherPackage = device.getLauncherPackageName();
            assertThat(launcherPackage, notNullValue());
            device.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)),
                    LAUNCH_TIMEOUT);

            // Launch the app
            Context context = ApplicationProvider.getApplicationContext();
            final Intent intent = context.getPackageManager()
                    .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE);
            // Clear out any previous instances
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
            context.startActivity(intent);

            // Wait for the app to appear
            device.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)),
                    LAUNCH_TIMEOUT);
        }
    }
    

Dalam contoh ini, pernyataan @SdkSuppress(minSdkVersion = 18) membantu memastikan bahwa pengujian hanya akan berjalan pada perangkat yang menggunakan Android 4.3 (API level 18) atau lebih tinggi, seperti yang diwajibkan oleh framework UI Automator.

Gunakan metode findObject() untuk mengambil UiObject yang merepresentasikan tampilan yang cocok dengan kriteria pemilih yang diberikan. Anda dapat menggunakan kembali instance UiObject yang telah Anda buat di bagian lain dari pengujian aplikasi Anda, sesuai kebutuhan. Perhatikan bahwa framework pengujian UI Automator mencari tampilan saat ini untuk pencocokan setiap kali pengujian Anda menggunakan instance UiObject untuk mengklik elemen UI atau mengkueri properti.

Cuplikan berikut menunjukkan bagaimana pengujian dapat membuat instance UiObject yang merepresentasikan tombol Batal dan tombol OK di aplikasi.

Kotlin

    val cancelButton: UiObject = device.findObject(
            UiSelector().text("Cancel").className("android.widget.Button")
    )
    val okButton: UiObject = device.findObject(
            UiSelector().text("OK").className("android.widget.Button")
    )

    // Simulate a user-click on the OK button, if found.
    if (okButton.exists() && okButton.isEnabled) {
        okButton.click()
    }
    

Java

    UiObject cancelButton = device.findObject(new UiSelector()
            .text("Cancel")
            .className("android.widget.Button"));
    UiObject okButton = device.findObject(new UiSelector()
            .text("OK")
            .className("android.widget.Button"));

    // Simulate a user-click on the OK button, if found.
    if(okButton.exists() && okButton.isEnabled()) {
        okButton.click();
    }
    

Menentukan pemilih

Jika Anda ingin mengakses komponen UI tertentu dalam aplikasi, gunakan class UiSelector. Class ini merepresentasikan kueri untuk elemen tertentu di UI yang saat ini ditampilkan.

Jika elemen pencocokan yang ditemukan lebih dari satu, elemen pencocokan pertama dalam hierarki tata letak ditampilkan sebagai target UiObject. Saat membuat UiSelector, Anda dapat mengaitkan beberapa properti untuk mempersempit penelusuran. Jika tidak ditemukan elemen UI yang cocok, UiAutomatorObjectNotFoundException akan dimunculkan.

Anda dapat menggunakan metode childSelector() untuk menyarangkan beberapa instance UiSelector. Misalnya, contoh kode berikut ini menunjukkan bagaimana pengujian Anda dapat menentukan penelusuran untuk menemukan ListView pertama di UI yang saat ini ditampilkan, lalu menelusuri di dalam ListView untuk menemukan elemen UI dengan Aplikasi properti teks.

Kotlin

    val appItem: UiObject = device.findObject(
            UiSelector().className("android.widget.ListView")
                    .instance(0)
                    .childSelector(
                            UiSelector().text("Apps")
                    )
    )
    

Java

    UiObject appItem = device.findObject(new UiSelector()
            .className("android.widget.ListView")
            .instance(0)
            .childSelector(new UiSelector()
            .text("Apps")));
    

Sebagai praktik terbaik, saat menentukan pemilih, sebaiknya gunakan ID Resource (jika ada yang ditetapkan untuk elemen UI), bukan elemen teks atau deskriptor konten. Tidak semua elemen memiliki atribut teks (misalnya, ikon di toolbar). Pemilih teks bersifat rapuh dan dapat menyebabkan kegagalan pengujian jika terdapat perubahan kecil pada UI. Pemilih teks juga dapat tidak menskala di berbagai bahasa; pemilih teks Anda mungkin tidak cocok dengan string yang diterjemahkan.

Hal tersebut mungkin berguna untuk menentukan status objek dalam kriteria pemilih Anda. Misalnya, jika ingin memilih daftar semua elemen yang dicentang sehingga Anda dapat menghapus centangnya, panggil metode checked() dengan argumen yang ditetapkan ke true.

Menjalankan tindakan

Setelah pengujian menerima objek UiObject, Anda dapat memanggil metode dalam class UiObject untuk menjalankan interaksi pengguna pada komponen UI yang direpresentasikan oleh objek tersebut. Anda dapat menentukan tindakan seperti:

  • click(): Mengklik di tengah bagian yang terlihat dari elemen UI.
  • dragTo(): Menarik objek ini ke koordinat arbitrer.
  • setText() : Menyetel teks dalam kolom yang dapat diedit, setelah menghapus konten kolom. Sebaliknya, metode clearTextField() menghapus teks yang ada di kolom yang dapat diedit.
  • swipeUp(): Menjalankan tindakan geser ke atas pada UiObject. Begitu juga, metode swipeDown(), swipeLeft(), dan swipeRight() akan menjalankan tindakan yang sesuai.

Framework pengujian UI Automator memungkinkan Anda mengirim Intent atau meluncurkan Activity tanpa menggunakan perintah shell, dengan mendapatkan objek Context melalui getContext().

Cuplikan berikut menunjukkan bagaimana pengujian Anda dapat menggunakan Intent untuk meluncurkan aplikasi yang sedang diuji. Pendekatan ini berguna saat Anda hanya tertarik untuk menguji aplikasi kalkulator, dan tidak memerlukan peluncur.

Kotlin

    fun setUp() {
        ...

        // Launch a simple calculator app
        val context = getInstrumentation().context
        val intent = context.packageManager.getLaunchIntentForPackage(CALC_PACKAGE).apply {
            addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
        }
        // Clear out any previous instances
        context.startActivity(intent)
        device.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT)
    }
    

Java

    public void setUp() {
        ...

        // Launch a simple calculator app
        Context context = getInstrumentation().getContext();
        Intent intent = context.getPackageManager()
                .getLaunchIntentForPackage(CALC_PACKAGE);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);

        // Clear out any previous instances
        context.startActivity(intent);
        device.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT);
    }
    

Menjalankan tindakan pada koleksi

Gunakan class UiCollection jika ingin menyimulasikan interaksi pengguna pada koleksi item (misalnya, lagu dalam suatu album musik atau daftar email di Kotak Masuk). Untuk membuat objek UiCollection, tetapkan UiSelector yang akan menelusuri container UI atau wrapper elemen UI turunan lainnya, misalnya tampilan tata letak yang berisi elemen UI turunan.

Cuplikan kode berikut menunjukkan bagaimana pengujian Anda dapat membuat UiCollection untuk merepresentasikan album video yang ditampilkan dalam FrameLayout:

Kotlin

    val videos = UiCollection(UiSelector().className("android.widget.FrameLayout"))

    // Retrieve the number of videos in this collection:
    val count = videos.getChildCount(
            UiSelector().className("android.widget.LinearLayout")
    )

    // Find a specific video and simulate a user-click on it
    val video: UiObject = videos.getChildByText(
            UiSelector().className("android.widget.LinearLayout"),
            "Cute Baby Laughing"
    )
    video.click()

    // Simulate selecting a checkbox that is associated with the video
    val checkBox: UiObject = video.getChild(
            UiSelector().className("android.widget.Checkbox")
    )
    if (!checkBox.isSelected) checkBox.click()
    

Java

    UiCollection videos = new UiCollection(new UiSelector()
            .className("android.widget.FrameLayout"));

    // Retrieve the number of videos in this collection:
    int count = videos.getChildCount(new UiSelector()
            .className("android.widget.LinearLayout"));

    // Find a specific video and simulate a user-click on it
    UiObject video = videos.getChildByText(new UiSelector()
            .className("android.widget.LinearLayout"), "Cute Baby Laughing");
    video.click();

    // Simulate selecting a checkbox that is associated with the video
    UiObject checkBox = video.getChild(new UiSelector()
            .className("android.widget.Checkbox"));
    if(!checkBox.isSelected()) checkbox.click();
    

Menjalankan tindakan pada tampilan yang dapat di-scroll

Gunakan class UiScrollable untuk menyimulasikan scroll vertikal atau horizontal pada tampilan. Teknik ini berguna bila elemen UI ditempatkan di belakang layar dan Anda perlu men-scroll untuk menampilkannya.

Cuplikan kode berikut menunjukkan cara menyimulasikan scroll ke bawah menu Setelan dan mengklik opsi Tentang tablet:

Kotlin

    val settingsItem = UiScrollable(UiSelector().className("android.widget.ListView"))
    val about: UiObject = settingsItem.getChildByText(
            UiSelector().className("android.widget.LinearLayout"),
            "About tablet"
    )
    about.click()
    

Java

    UiScrollable settingsItem = new UiScrollable(new UiSelector()
            .className("android.widget.ListView"));
    UiObject about = settingsItem.getChildByText(new UiSelector()
            .className("android.widget.LinearLayout"), "About tablet");
    about.click();
    

Memverifikasi hasil

InstrumentationTestCase memperluas TestCase, sehingga Anda dapat menggunakan metode Assert JUnit standar untuk menguji bahwa komponen UI dalam aplikasi menampilkan hasil yang diharapkan.

Cuplikan berikut menunjukkan bagaimana pengujian Anda dapat menemukan beberapa tombol di aplikasi kalkulator, mengklik tombol tersebut secara berurutan, lalu memverifikasi bahwa hasil yang benar ditampilkan.

Kotlin

    private const val CALC_PACKAGE = "com.myexample.calc"

    fun testTwoPlusThreeEqualsFive() {
        // Enter an equation: 2 + 3 = ?
        device.findObject(UiSelector().packageName(CALC_PACKAGE).resourceId("two")).click()
        device.findObject(UiSelector().packageName(CALC_PACKAGE).resourceId("plus")).click()
        device.findObject(UiSelector().packageName(CALC_PACKAGE).resourceId("three")).click()
        device.findObject(UiSelector().packageName(CALC_PACKAGE).resourceId("equals")).click()

        // Verify the result = 5
        val result: UiObject2 = device.findObject(By.res(CALC_PACKAGE, "result"))
        assertEquals("5", result.text)
    }
    

Java

    private static final String CALC_PACKAGE = "com.myexample.calc";

    public void testTwoPlusThreeEqualsFive() {
        // Enter an equation: 2 + 3 = ?
        device.findObject(new UiSelector()
                .packageName(CALC_PACKAGE).resourceId("two")).click();
        device.findObject(new UiSelector()
                .packageName(CALC_PACKAGE).resourceId("plus")).click();
        device.findObject(new UiSelector()
                .packageName(CALC_PACKAGE).resourceId("three")).click();
        device.findObject(new UiSelector()
                .packageName(CALC_PACKAGE).resourceId("equals")).click();

        // Verify the result = 5
        UiObject result = device.findObject(By.res(CALC_PACKAGE, "result"));
        assertEquals("5", result.getText());
    }
    

Menjalankan pengujian UI Automator pada perangkat atau emulator

Anda dapat menjalankan pengujian UI Automator dari Android Studio atau dari command line. Pastikan untuk menentukan AndroidJUnitRunner sebagai runner instrumentasi default di project Anda.

Referensi lainnya

Untuk mengetahui informasi selengkapnya tentang penggunaan UI Automator dalam pengujian Android, lihat referensi berikut.

Contoh

Codelab