Membuat pengujian unit lokal

Logika aplikasi dapat dievaluasi menggunakan pengujian unit lokal saat Anda perlu menjalankan pengujian lebih cepat dan tidak memerlukan fidelitas serta keyakinan yang terkait dengan menjalankan pengujian di perangkat sebenarnya. Dengan pendekatan ini, Anda biasanya dapat memenuhi hubungan dependensi menggunakan Robolectric atau framework tiruan, seperti Mockito. Biasanya, jenis dependensi yang terkait dengan pengujian menentukan fitur mana yang Anda gunakan:

  • Jika memiliki dependensi pada framework Android, khususnya yang membuat interaksi kompleks dengan framework, sebaiknya Anda menyertakan dependensi framework menggunakan Robolectric.
  • Jika pengujian memiliki dependensi minimal pada framework Android, atau jika pengujian hanya bergantung pada objek Anda sendiri, Anda dapat menyertakan dependensi tiruan menggunakan framework tiruan seperti Mockito.

Menyiapkan lingkungan pengujian Anda

Dalam project Android Studio, Anda harus menyimpan file sumber untuk pengujian unit lokal di module-name/src/test/java/. Direktori ini sudah ada saat Anda membuat project baru.

Anda juga perlu mengonfigurasi dependensi pengujian untuk project Anda agar dapat menggunakan API standar yang diberikan oleh framework JUnit 4. Jika pengujian Anda perlu berinteraksi dengan dependensi Android, sertakan Robolectric atau library Mockito untuk menyederhanakan pengujian unit lokal Anda.

Di file build.gradle tingkat teratas aplikasi, tentukan library berikut sebagai dependensi:

    dependencies {
        // Required -- JUnit 4 framework
        testImplementation 'junit:junit:4.12'
        // Optional -- Robolectric environment
        testImplementation 'androidx.test:core:1.0.0'
        // Optional -- Mockito framework
        testImplementation 'org.mockito:mockito-core:1.10.19'
    }
    

Membuat class pengujian unit lokal

Class pengujian unit lokal Anda harus ditulis sebagai class pengujian JUnit 4. JUnit adalah framework pengujian unit yang paling populer dan banyak digunakan untuk Java. JUnit 4 memungkinkan Anda untuk menulis pengujian dengan cara yang lebih rapi dan fleksibel daripada versi pendahulunya karena JUnit 4 tidak mengharuskan Anda untuk melakukan hal-hal berikut:

  • Meluaskan class junit.framework.TestCase.
  • Memberikan awalan pada nama metode pengujian Anda dengan kata kunci 'test'.
  • Menggunakan class dari paket junit.framework atau junit.extensions.

Untuk membuat class pengujian JUnit 4 dasar, buat class yang berisi satu atau beberapa metode pengujian. Sebuah metode pengujian dimulai dengan anotasi @Test dan berisi kode untuk menerapkan serta memverifikasi fungsionalitas tunggal dalam komponen yang ingin Anda uji.

Contoh berikut menunjukkan cara mengimplementasikan class pengujian unit lokal. Metode pengujian emailValidator_CorrectEmailSimple_ReturnsTrue memverifikasi bahwa metode isValidEmail() di aplikasi yang diuji menunjukkan hasil yang benar.

Kotlin

    import com.google.common.truth.Truth.assertThat
    import org.junit.Test

    class EmailValidatorTest {
        @Test
        fun emailValidator_CorrectEmailSimple_ReturnsTrue() {
            assertThat(EmailValidator.isValidEmail("name@email.com")).isTrue()
        }
    }
    

Java

    import com.google.common.truth.Truth.assertThat;
    import org.junit.Test;

    public class EmailValidatorTest {
        @Test
        public void emailValidator_CorrectEmailSimple_ReturnsTrue() {
            assertThat(EmailValidator.isValidEmail("name@email.com")).isTrue();
        }
    }
    

Untuk membuat pengujian dapat dibaca yang mengevaluasi apakah komponen-komponen dalam aplikasi Anda menunjukkan hasil yang diharapkan, kami sarankan untuk menggunakan library Truth dan class dari Android Assertion, seperti yang ditunjukkan di contoh sebelumnya. Untuk mempelajari lebih lanjut jenis validasi logika yang didukung oleh Truth dan Android Assertion, lihat bagian yang menjelaskan cara membuat pernyataan yang lebih mudah dibaca.

Jika membandingkan antara hasil yang diharapkan dengan hasil sebenarnya menggunakan metode junit.Assert atau matcher Hamcrest (seperti metode is() dan equalTo()) lebih mudah, Anda dapat menggunakan library tersebut sebagai gantinya.

Catatan: Hamcrest masih menjadi library yang lebih disukai untuk digunakan saat membuat matcher, seperti untuk class ViewMatcher Espresso.

Menyertakan dependensi framework

Jika pengujian Anda berinteraksi dengan beberapa dependensi framework Android, atau berinteraksi bersama dependensi tersebut dengan cara yang kompleks, gunakan artefak Robolectric yang diberikan oleh Pengujian AndroidX. Robolectric menjalankan kode framework Android asli dan kode framework native palsu di JVM lokal atau di perangkat sebenarnya.

Contoh berikut menunjukkan cara membuat pengujian unit yang menggunakan Robolectric:

app/build.gradle

    android {
        // ...
        testOptions {
            unitTests.includeAndroidResources = true
        }
    }
    

MyLocalUnitTestClass

Kotlin

    import android.content.Context
    import androidx.test.core.app.ApplicationProvider
    import com.google.common.truth.Truth.assertThat
    import org.junit.Test

    private const val FAKE_STRING = "HELLO_WORLD"

    class UnitTestSample {
        val context = ApplicationProvider.getApplicationContext<Context>()

        @Test fun readStringFromContext_LocalizedString() {
            // Given a Context object retrieved from Robolectric...
            val myObjectUnderTest = ClassUnderTest(context)

            // ...when the string is returned from the object under test...
            val result: String = myObjectUnderTest.getHelloWorldString()

            // ...then the result should be the expected one.
            assertThat(result).isEqualTo(FAKE_STRING)
        }
    }
    

Java

    import android.content.Context;
    import androidx.test.core.app.ApplicationProvider;
    import org.junit.Test;

    import static com.google.common.truth.Truth.assertThat;

    public class UnitTestSampleJava {
        private static final String FAKE_STRING = "HELLO_WORLD";
        private Context context = ApplicationProvider.getApplicationContext();

        @Test
        public void readStringFromContext_LocalizedString() {
            // Given a Context object retrieved from Robolectric...
            ClassUnderTest myObjectUnderTest = new ClassUnderTest(context);

            // ...when the string is returned from the object under test...
            String result = myObjectUnderTest.getHelloWorldString();

            // ...then the result should be the expected one.
            assertThat(result).isEqualTo(FAKE_STRING);
        }
    }
    

Menyertakan class Android Builder

Jika membuat pengujian unit lokal yang dijalankan di lingkungan Robolectric atau di perangkat sebenarnya, Anda dapat menggunakan builder yang disediakan Pengujian AndroidX untuk beberapa class framework umum. Builder tersebut memungkinkan Anda untuk membuat instance dari class berikut tanpa perlu menggunakan tiruan atau refleksi:

Menggunakan class utilitas Parcelables

Selain itu, library juga memberikan class utilitas untuk objek Parcelable. Dengan memberikan objek Creator, class ini melakukan unmarshal kepada objek Parcelable yang diberikan, lalu melakukan marshal kepada objek Parcelable duplikat.

Catatan: Metode yang memanggil Parcelables.forceParcel() akan menjadi penentu apakah operasi unmarshal/remarshal telah berhasil atau tidak.

Menyertakan dependensi tiruan

Secara default, Android Gradle Plugin menjalankan pengujian unit lokal Anda terhadap versi modifikasi library android.jar, yang tidak berisi kode aktual. Sebagai gantinya, metode yang memanggil ke class Android dari pengujian unit Anda akan memunculkan pengecualian. Perilaku ini memastikan Anda hanya menguji kode Anda dan tidak bergantung pada perilaku tertentu dari platform Android (yang belum dibuat atau ditiru secara eksplisit).

Meniru dependensi Android

Jika Anda memiliki dependensi Android minimal serta perlu menguji interaksi spesifik antara komponen dan dependensinya dalam aplikasi, gunakan framework tiruan untuk menghentikan dependensi eksternal dalam kode Anda. Dengan begitu, Anda dapat dengan mudah menguji apakah komponen berinteraksi dengan dependensi menggunakan cara yang diharapkan. Dengan mengganti dependensi Android dengan objek tiruan, Anda dapat memisahkan pengujian unit dari sistem Android lainnya sambil memverifikasi bahwa metode yang benar dalam dependensi tersebut dipanggil. Framework tiruan Mockito untuk Java (versi 1.9.5 dan yang lebih tinggi) menawarkan kompatibilitas dengan pengujian unit Android. Dengan Mockito, Anda dapat mengonfigurasi objek tiruan untuk menampilkan nilai tertentu saat dipanggil.

Untuk menambahkan objek tiruan ke pengujian unit lokal Anda menggunakan framework ini, ikuti model pemrograman berikut:

  1. Sertakan dependensi library Mockito dalam file build.gradle, seperti yang dijelaskan dalam Menyiapkan lingkungan pengujian Anda.
  2. Di awal definisi class pengujian unit, tambahkan anotasi @RunWith(MockitoJUnitRunner.class). Anotasi ini memberi tahu runner pengujian Mockito untuk memvalidasi bahwa penggunaan framework sudah benar dan menyederhanakan inisialisasi objek tiruan Anda.
  3. Untuk membuat objek tiruan untuk dependensi Android, tambahkan anotasi @Mock sebelum deklarasi field.
  4. Untuk menghentikan perilaku dependensi, Anda dapat menentukan kondisi dan mengembalikan nilai saat kondisi terpenuhi dengan menggunakan metode when() dan thenReturn().

Contoh berikut menunjukkan cara membuat pengujian unit yang menggunakan objek Context tiruan.

Kotlin

    import android.content.Context
    import com.google.common.truth.Truth.assertThat
    import org.junit.Test
    import org.junit.runner.RunWith
    import org.mockito.Mock
    import org.mockito.Mockito.`when`
    import org.mockito.junit.MockitoJUnitRunner

    private const val FAKE_STRING = "HELLO WORLD"

    @RunWith(MockitoJUnitRunner::class)
    class UnitTestSample {

        @Mock
        private lateinit var mockContext: Context

        @Test
        fun readStringFromContext_LocalizedString() {
            // Given a mocked Context injected into the object under test...
            `when`(mockContext.getString(R.string.hello_word))
                    .thenReturn(FAKE_STRING)
            val myObjectUnderTest = ClassUnderTest(mockContext)

            // ...when the string is returned from the object under test...
            val result: String = myObjectUnderTest.getHelloWorldString()

            // ...then the result should be the expected one.
            assertThat(result, `is`(FAKE_STRING))
        }
    }
    

Java

    import android.content.Context;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.Mock;
    import org.mockito.junit.MockitoJUnitRunner;

    import static com.google.common.truth.Truth.assertThat;
    import static org.mockito.Mockito.when;

    @RunWith(MockitoJUnitRunner.class)
    public class UnitTestSample {

        private static final String FAKE_STRING = "HELLO WORLD";

        @Mock
        Context mockContext;

        @Test
        public void readStringFromContext_LocalizedString() {
            // Given a mocked Context injected into the object under test...
            when(mockContext.getString(R.string.hello_world))
                    .thenReturn(FAKE_STRING);
            ClassUnderTest myObjectUnderTest = new ClassUnderTest(mockContext);

            // ...when the string is returned from the object under test...
            String result = myObjectUnderTest.getHelloWorldString();

            // ...then the result should be the expected one.
            assertThat(result, is(FAKE_STRING));
        }
    }
    

Untuk mempelajari lebih lanjut cara menggunakan framework Mockito, lihat Referensi Mockito API dan class SharedPreferencesHelperTest dalam kode contoh. Coba juga Android Testing Codelab.

Error: "Metode ... tidak ditiru"

Jika Anda menjalankan pengujian yang memanggil API dari Android SDK yang tidak Anda tiru, error yang memberitahukan metode ini tidak ditiru akan muncul. Ini terjadi karena file android.jar yang digunakan untuk menjalankan pengujian unit tidak berisi kode aktual (API tersebut hanya diberikan oleh image sistem Android pada perangkat).

Sebagai gantinya, semua metode akan memunculkan pengecualian secara default. Perilaku ini memastikan pengujian unit hanya menguji kode Anda dan tidak bergantung pada perilaku tertentu dari platform Android (yang belum Anda tiru secara eksplisit, seperti dengan Mockito).

Jika pengecualian yang dimunculkan bermasalah bagi pengujian, Anda dapat mengubah perilaku tersebut sehingga metode akan menampilkan null atau nol dengan menambahkan konfigurasi berikut dalam file build.gradle tingkat teratas project Anda:

    android {
      ...
      testOptions {
        unitTests.returnDefaultValues = true
      }
    }
    

Perhatian: Menyetel properti returnDefaultValues ke true harus dilakukan dengan hati-hati. Nilai-nilai pengembalian null/nol dapat menyebabkan regresi dalam pengujian Anda, yang sulit untuk di-debug dan mungkin meluluskan pengujian yang gagal. Hanya gunakan langkah tersebut sebagai pilihan terakhir.

Menjalankan pengujian unit lokal

Untuk menjalankan pengujian unit lokal, gunakan langkah-langkah berikut:

  1. Pastikan project Anda disinkronkan bersama Gradle dengan mengklik Sync Project di toolbar.
  2. Untuk menjalankan pengujian, lakukan salah satu cara berikut:
    • Untuk menjalankan pengujian tunggal, buka jendela Project, lalu klik kanan pengujian dan klik Run .
    • Untuk menguji semua metode di suatu class, klik kanan class atau metode di file pengujian dan klik Run .
    • Untuk menjalankan semua pengujian di suatu direktori, klik-kanan pada direktori dan pilih Run tests .

Android Gradle Plugin mengompilasi kode pengujian unit lokal yang terletak di direktori default (src/test/java/), membuat aplikasi pengujian, dan menjalankannya secara lokal menggunakan class runner pengujian default. Android Studio kemudian menampilkan hasilnya di jendela Run.