Melakukan Unit Testing Di Android


Walaupun sudah memakai class, aplikasi yang saya buat di artikel Belajar Memakai Implicit Intent Di Android dirancang dengan mengikuti filosofi prosedural. Ini adalah cara yang paling sering saya pakai saat mempelajari sesuatu yang baru karena dengan cara ini, saya bisa menelusuri aliran eksekusi program secara jelas. Akan tetapi, pada aplikasi nyata yang kompleks, melakukan pemisahan atau pengkategorian berdasarkan unit yang disebut class akan memberikan lebih banyak keuntungan. Salah satunya adalah aplikasi menjadi lebih mudah diuji.

Sebagai contoh, saya memisahkan beberapa fungsi yang sebelumnya tersebar di activity dan meletakkannya ke dalam sebuah domain class yang saya beri nama snake.com.myhexviewer.Viewer yang isinya seperti berikut ini:

public class Viewer {

    byte[] data

    public Viewer(byte[] data) {
        this.data = data
    }

    public int getMaxPages() {
        data? (data.length / 1024): 0
    }

    public String getHexDumpForPage(int pageNumber) {
        ... // sama seperti sebelumnya
    }

}

Di activity, saya dapat memakai kode program seperti viewer.getHexDumpForPage(progress) untuk menampilkan hasil hexdump.

Sebagai latihan, saya akan menambahkan sebuah fasilitas untuk melakukan pencarian kombinasi huruf dan angka dengan minimal jumlah karakter tertentu. Hal ini sangat berguna untuk mencari informasi yang dapat dibaca di sebuah file. Untuk itu, saya menambahkan sebuah method baru dengan nama findStrings() seperti berikut ini:

public class Viewer {

    byte[] data

    public Viewer(byte[] data) {
        this.data = data
    }

    public int getMaxPages() {
        data? (data.length / 1024): 0
    }

    public String getHexDumpForPage(int pageNumber) {
        ... // sama seperti sebelumnya
    }

    public List<String> findStrings(int minimumLength) {
        ... // kode program diabaikan
    }

}

Method findStrings() akan mengembalikan sebuah array yang berisi daftar string yang ditemukan di dalam sebuah file. Kode program yang ada didalamnya cukup rumit dan saya tidak yakin sepenuhnya bahwa kode program tersebut benar! Saya akan melakukan pendekatan trial dan error sampai saya memperoleh hasil yang diharapkan! Selama pendekatan trial dan error, saya harus bisa menguji program.

Melakukan pengujian secara manual dengan menjalankan aplikasi berulang kali akan sangat melelahkan. Apakah ada cara yang lebih singkat? Yup! Saya dapat membuat sebuah test case yang berisi skenario pengujian. Sebagai contoh, saya membuat sebuah class baru dengan nama snake.com.myhexviewer.domain.ViewerTest di folder app/src/androidTest/groovy yang isinya seperti berikut ini:

package snake.com.myhexviewer.domain

import android.app.Application
import android.test.ApplicationTestCase
import snake.com.myhexviewer.Viewer

public class ViewerTest extends ApplicationTestCase<Application> {

    Viewer viewer

    public ViewerTest() {
        super(Application)
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp()
        viewer = new Viewer([
            0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08,
            0x08, 0x00, 0x82, 0x50, 0x33, 0x46, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x5b, 0x43,
            0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x54,
            0x79, 0x70, 0x65, 0x73, 0x5d, 0x2e, 0x78, 0x6d,
            0x6c, 0xcd, 0x54, 0xdb, 0x6e, 0xc2, 0x30, 0x0c,
        ] as byte[])
    }

    public void testFindStrings() {
        List<String> hasil = viewer.findStrings(0)
        assertTrue(hasil.empty)

        hasil = viewer.findStrings(2)
        assertEquals(5, hasil.size())
        assertEquals(hasil[0], 'PK')
        assertEquals(hasil[1], 'P3F')
        assertEquals(hasil[2], 'Content')
        assertEquals(hasil[3], 'Types')
        assertEquals(hasil[4], 'xml')

        hasil = viewer.findStrings(3)
        assertEquals(4, hasil.size())
        assertEquals(hasil[0], 'P3F')
        assertEquals(hasil[1], 'Content')
        assertEquals(hasil[2], 'Types')
        assertEquals(hasil[3], 'xml')

        hasil = viewer.findStrings(4)
        assertEquals(2, hasil.size())
        assertEquals(hasil[0], 'Content')
        assertEquals(hasil[1], 'Types')

        hasil = viewer.findStrings(6)
        assertEquals(1, hasil.size())
        assertEquals(hasil[0], 'Content')

        hasil = viewer.findStrings(8)
        assertTrue(hasil.empty)
    }

}

Pada saat saya menulis skenario pengujian di atas, saya bahkan belum mengetik kode program yang benar di dalam method findStrings(). Ini adalah praktek yang sering disebut sebagai Test Driven Development (TDD). Landasan dari TDD adalah pandangan dimana programmer biasanya menganalisa input dan output terlebih dahulu sebelum merancang proses. Dengan menulis pengujian terlebih dahulu, programmer mencari input yang mungkin diberikan dan output yang diharapkan sebelum membuat kode program.

Berbeda dengan program Java, pengujian di Android harus dilakukan pada emulator atau perangkat. Hal ini karena program Android dijalankan pada ART (atau Dalvik) yang berbeda dari Java Virtual Machine. Sebagai contoh, semua JVM baik dari Oracle maupun pihak ketiga (seperti OpenJDK) pasti mendukung Java SE yang memiliki class GUI seperti Swing (seperti javax.swing.JButton) dan engine scripting (seperti javax.script.ScriptEngine). Akan tetapi tidak demikian halnya dengan Android SDK! Sebaliknya, Android SDK memiliki konsep activity, fragment, membatasi komunikasi dengan Parcelable dan sejenisnya yang tidak dijumpai di Java SE, Java ME maupun Java EE. Ini juga yang menjadi penyebab tuntutan Oracle kepada Google: Android dianggap menciptakan Java ‘jenis baru’ yang tidak cocok dengan Java yang sudah ada. Programmer Java sering tertipu karena mengira write once run anywhere berlaku di Android😀

Untuk menjalankan pengujian, saya akan memilih drop down Select Run/Debug Configurations di toolbar Android Studio dan men-klik menu Edit Configurations. Saya kemudian men-klik tombol plus dan memilih JUnit seperti pada gambar berikut ini:

Menambah launch configuration baru

Menambah launch configuration baru

Saya kemudian mengisi dialog yang muncul dengan informasi seperti berikut ini:

Launch configuration untuk pengujian Android

Launch configuration untuk pengujian Android

Setelah itu, saya men-klik tombol Ok. Sekarang, saya dapat memilih untuk menjalankan aplikasi atau hanya menjalankan pengujian di Select Run/Debug Configurations, seperti yang terlihat pada gambar berikut ini:

Memilih launch configuration

Memilih launch configuration

Bila saya menjalankan pengujian, saya akan memperoleh hasil seperti pada gambar berikut ini:

Hasil pengujian yang gagal

Hasil pengujian yang gagal

Pengujian tidak sukses karena saya belum mengimplementasikan kode program dengan baik. Oleh sebab itu, saya segera melakukan implementasi kode program dan kembali melakukan pengujian. Bila pengujian gagal, saya akan memperbaiki kode program dan kembali menjalankan pengujian. Demikian seterusnya sampai saya memperoleh hasil seperti pada gambar berikut ini:

Hasil pengujian yang sukses

Hasil pengujian yang sukses

Salah satu keuntungan membuat pengujian adalah saya tidak dipusingkan dengan UI. Saya tidak perlu mengisi input dan men-klik tombol untuk men-ngetes kode program. Proses debugging juga bisa langsung dilakukan, misalnya saya bisa menghentikan eksekusi pada baris yang dikehendaki.

Sampai disini, saya bisa yakin bahwa kemungkinan besar kode program akan bekerja dengan baik. Saya hanya perlu melanjutkan ke bagian view seperti membuat fragment baru dan memikirkan bagaimana mengakses object Viewer yang sama dari beberapa fragment yang berbeda.

Menguji sebuah class yang benar-benar tidak terkait dengan Android dengan cara seperti di atas adalah sesuatu yang mubazir, bukan? Akan lebih baik bila saya bisa menjalankan pengujian memakai TestCase bawaan JUnit (tanpa harus membuat proyek baru). Berdasarkan informasi di http://tools.android.com/tech-docs/unit-testing-support, Android Studio dan Android plugin for Gradle versi 1.1 akan mendukung pengujian melalui JVM lokal tanpa melalui emulator atau perangkat. Untuk saat ini, fasilitas tersebut masih bersifat eksperimental.

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: