news

Senin, 15 Juni 2020

Belajar Pengujian ViewModel dengan Unit Testing


Tujuan

Pada Codelab kali ini Anda akan menggunakan Junit untuk melakukan testing pada aplikasi MyViewModel yang sudah dibuat. Poin penting materi ini adalah agar Anda mengerti bagaimana menggunakan JUnit untuk melakukan pengujian pada aplikasi Anda.


Logika Dasar

Menjalankan Aplikasi → menguji secara manual → menguji dengan Unit Testing → mendapat hasil pengujian secara otomatis.

Codelab Pengujian ViewModel dengan Unit Testing

  1. Anda bisa membuka proyek sebelumnya, MyViewModel atau unduh di tautan berikut:
    MyViewModel
  2. Tambahkan terlebih dahulu library Unit Testing ke dalam project Anda. Bukalah build.gradle , masukkan kode berikut di dalamnya, dan jangan lupa untuk Sync Project.
    testImplementation 'junit:junit:4.12'
    testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.1.0'
  3. Selanjutnya, buat kelas baru untuk melakukan pengujian. Untuk membuatnya secara otomatis, buka kelas MainViewModel kemudian klik Alt+Enter pada MainViewModel maka akan ada banyak pilihan seperti berikut:
    Kotlin
    20191127112949de240e32d1bc8beab97382567e3fe87e.pngSetelah itu, pilih Create Test.
    201911271131112bf29738cd10bb160537adf94b817d44.png
    Java
    20191127113450491c4c751553b4674e81587fbc0dd763.pngSetelah itu, pilih Create Test.2019112711360417759d616301a5f1a1530184430ae534.png
    Anda perlu memberi checklist pada method calculate pada gambar di atas. Biarkan default nama kelas yang baru, MainViewModelTest. Klik OK untuk melanjutkan.

  4. Setelah itu, Anda diminta untuk menentukan di mana lokasi MainViewModelTest akan dibentuk. Pilihlah .../test/.... Mengapa demikian? Directory test digunakan untuk unit Testing, sedangkan directory androidTest digunakan untuk instrumental testing yang nanti akan dibahas pada modul selanjutnya. Klik OK untuk melanjutkan.
    20191127113736db2a88a288b14c589378e105a5958cf1.png
  5. Maka secara otomatis akan ada satu berkas baru seperti ini:
    2019053002141595a237c6cdbff3cc509a887c65ba8670Sesuai dengan directory yang Anda pilih sebelumnya, kelas MainViewModelTest berada pada directory test.
  6. Ok, sekarang buka kelas MainViewModelTest. Jika dilihat kode hasil generator menjadi seperti ini:

Kotlin
class MainViewModelTest {

@Test
fun calculate() {
}
}
Java
public class MainViewModelTest {

@Test
public void calculate() {
}
}
Pada kode di atas sudah ada metode calculate yang digunakan untuk menguji metode yang sama pada kelas MainViewModel.
  1. Alangkah baiknya kita buka kembali kelas MainViewModel agar Anda paham apa yang mau diuji. Seperti ini kode pada kelas MainViewModel:
    Kotlin
    class MainViewModel : ViewModel() {
    var result = 0

    fun calculate(width: String, height: String, length: String) {
    result = width.toInt() * height.toInt() * length.toInt()
    }
    }
    Java
    public class MainViewModel extends ViewModel {
    int result = 0;

    void calculate(String width, String height, String length) {
    result = Integer.parseInt(width) * Integer.parseInt(height) * Integer.parseInt(length);
    }
    }
    Di dalam MainViewModel terdapat 1 variable result dan 1 metode calculate.
  2. Pertama, kita akan uji antara apa yang Anda masukan di calculate dengan hasil dari result. Tambahkan dan ubahlah kelas MainViewModelTest menjadi seperti ini:
    Kotlin
    class MainViewModelTest {
    private lateinit var mainViewModel: MainViewModel

    @Before
    fun init() {
    mainViewModel = MainViewModel()
    }

    @Test
    fun calculate() {
    val width = "1"
    val length = "2"
    val height = "3"
    mainViewModel.calculate(width, height, length)
    assertEquals(6, mainViewModel.result)
    }
    }
    Java
    public class MainViewModelTest {
    private MainViewModel mainViewModel;

    @Before
    public void init() {
    mainViewModel = new MainViewModel();
    }

    @Test
    public void calculate() {
    String width = "1";
    String length = "2";
    String height = "3";
    mainViewModel.calculate(width, height, length);
    assertEquals(6, mainViewModel.result);
    }
    }
    Kode di atas digunakan untuk memastikan apakah hasil dari mainViewModel.result sesuai dengan yang diharapkan.
  3. Oke, Anda bisa jalankan pengujian pertama Anda. Klik bagian ini untuk melakukan pengujian.
    201905300215452204310c32a3a306b65f9f6f1023d08bKemudian pilih Run ‘MainViewModelTest’.
  4. Android Studio akan menjalankan pengujian kelas MainViewModelTest dan akan menghasilkan skenario seperti ini.
    201905300216376b77616559a674e7626ec41185fc352dGambar di atas menunjukkan bahwa pengujian yang Anda lakukan berhasil. Itu bisa dilihat dengan adanya checklist berwarna hijau pada proses pengujian.
  5. Oke, selanjutnya kita Akan coba ketika inputan berbeda dengan ekspektasi. Ubahlah kode di kelas MainViewModelTest menjadi seperti ini:
    Kotlin
    class MainViewModelTest {
    private lateinit var mainViewModel: MainViewModel

    @Before
    fun init() {
    mainViewModel = MainViewModel()
    }

    @Test
    fun calculate() {
    val width = "1"
    val length = "2"
    val height = "2"
    mainViewModel.calculate(width, height, length)
    assertEquals(6, mainViewModel.result)
    }
    }
    Java
    public class MainViewModelTest {
    private MainViewModel mainViewModel;

    @Before
    public void init() {
    mainViewModel = new MainViewModel();
    }

    @Test
    public void calculate() {
    String width = "1";
    String length = "2";
    String height = "2";
    mainViewModel.calculate(width, height, length);
    assertEquals(6, mainViewModel.result);
    }
    }
  6. Jalankan pengujian tersebut, maka akan seperti ini:
    201905300218212a72baec268c06155a26eb2d2a023802Ekspektasi dari pengujian adalah 6, namun dalam pengujian aktualnya adalah 4. Tentu tak sesuai antara aktual dengan ekspektasi, maka pengujian dianggap gagal. Oke, Anda bisa sesuaikan kembali nilai height agar aktual dengan ekspektasi sama.
  7. Selanjutnya, kita akan coba input di dalam string menjadi double bukan integer. Tambahkan kode seperti ini:

    Kotlin

    class MainViewModelTest {
    ...

    @Test
    fun doubleInputCalculateTest() {
    val width = "1"
    val length = "2.4"
    val height = "3"
    mainViewModel.calculate(width, height, length)
    }
    }

    Java

    public class MainViewModelTest {
    ...

    @Test
    public void doubleInputCalculateTest() {
    String width = "1";
    String length = "2.4";
    String height = "3";
    mainViewModel.calculate(width, height, length);
    }
    }

  8. Jalankan pengujian tersebut, maka akan menjadi seperti ini:20190530022028f639b6edb1344f06a599c7413ae4902e.pngPengujian yang dilakukan gagal, karena apa yang Anda masukan dalam string bukanlah integer melainkan double. Ini mungkin saja terjadi dalam kejadian-kejadian tertentu, namun Anda perlu mengantisipasi kejadian tersebut.

    Kotlin

    class MainViewModelTest {
    ...

    @get:Rule
    var thrown = ExpectedException.none()


    ...

    @Test
    @Throws(NumberFormatException::class)
    fun doubleInputCalculateTest() {
    val width = "1"
    val length = "2.4"
    val height = "3"
    thrown.expect(NumberFormatException::class.java)
    thrown.expectMessage("For input string: \"2.4\"")

    mainViewModel.calculate(width, height, length)
    }
    }

    Java

    public class MainViewModelTest {
    ...

    @Rule
    public ExpectedException thrown = ExpectedException.none();


    ...

    @Test
    public void doubleInputCalculateTest() throws NumberFormatException {
    String width = "1";
    String length = "2.4";
    String height = "3";

    thrown.expect(NumberFormatException.class);
    thrown.expectMessage("For input string: \"2.4\"");

    mainViewModel.calculate(width, height, length);
    }
    }
    Annotation @Rule digunakan untuk untuk menjalankan kode sebelum pengujian dilakukan. Jadi jika tidak diberi anotasi @Rule pada thrown, maka kode tersebut tidak akan berjalan.
  9. Karena eror tersebut berasal dari NumberFormatException, maka Anda bisa menambahkan itu dengan cara seperti ini:
  10. Jalankan kode tersebut, maka hasilnya akan menjadi seperti ini:
    20190530022211e9d31045197f80546fdb7580b11de4d2Lihat hasil di atas! Pengujian Anda berhasil. Sesuai Ekspektasi Anda bahwa eror tersebut terjadi karena NumberFormatException, bahkan Anda juga mencocokkan pesan dari eror yang terjadi dengan ekspektasi Anda.
    NumberFormatException akan terjadi saat Anda mencoba mengubah String menjadi nilai angka namun String tersebut tidak terformat dengan benar. Misalnya dalam kasus tersebut ekspektasi Anda adalah Integer, namun aktualnya adalah String.
  11. Selanjutnya tambahkan kode berikut untuk memberi ekspektasi eror yang terjadi saat ada input kosong dan berupa character:

    Kotlin

    class MainViewModelTest {
    ...

    @Test
    @Throws(java.lang.NumberFormatException::class)
    fun characterInputCalculateTest() {
    val width = "1"
    val length = "A"
    val height = "3"
    thrown.expect(java.lang.NumberFormatException::class.java)
    thrown.expectMessage("For input string: \"A\"")
    mainViewModel.calculate(width, length, height)
    }


    @Test
    @Throws(java.lang.NumberFormatException::class)
    fun emptyInputCalculateTest() {
    val width = "1"
    val length = ""
    val height = "3"
    thrown.expect(java.lang.NumberFormatException::class.java)
    thrown.expectMessage("For input string: \"\"")
    mainViewModel.calculate(width, height, length)
    }
    }

    Java
    public class MainViewModelTest {
    ...

    @Test
    public void characterInputCalculateTest() throws NumberFormatException {
    String width = "1";
    String length = "A";
    String height = "3";
    thrown.expect(NumberFormatException.class);
    thrown.expectMessage("For input string: \"A\"");
    mainViewModel.calculate(width, length, height);
    }


    @Test
    public void emptyInputCalculateTest() throws NumberFormatException {
    String width = "1";
    String length = "";
    String height = "3";
    thrown.expect(NumberFormatException.class);
    thrown.expectMessage("For input string: \"\"");
    mainViewModel.calculate(width, height, length);
    }
    }
  12. Anda bisa menjalankan kembali pengujian yang Anda buat, maka akan menjadi seperti ini:
    201905300225180b3da29e4d7e3bd6b6cf851418846888Tentu, pengujian di atas berhasil dilakukan karena Anda sudah memberi ekspektasi eror yang terjadi. Untuk pengujian, Anda bisa melakukan sesuai dengan kebutuhan dari aplikasi Anda.

Bedah Kode

Kita sudah melewati beberapa latihan untuk melakukan UnitTest. Pada modul sebelumnya juga Anda sudah mempelajari tentang Unit Test.

Perhatikan kode berikut:

Kotlin

private lateinit var mainViewModel: MainViewModel

@Before
fun init() {
mainViewModel = MainViewModel()
}


Java


private MainViewModel mainViewModel;

@Before
public void init() {
mainViewModel = new MainViewModel();
}

Untuk menginisialisasi ViewModel, Anda perlu melakukannya di dalam method init() dengan anotasi @Before. Anotasi ini berfungsi untuk melakukan serangkaian persiapan sebelum melakukan pengujian.

Perhatikan kode berikut:

Kotlin

@Test
fun calculate() {
val width = "1"
val length = "2"
val height = "3"
mainViewModel.calculate(width, height, length)
assertEquals(6, mainViewModel.result)
}



Java

@Test
public void calculate() {
String width = "1";
String length = "2";
String height = "3";
mainViewModel.calculate(width, height, length);
assertEquals(6, mainViewModel.result);
}


Kode assertEquals berguna untuk membandingkan antara ekspektasi dan hasil aktual perhitungan. Jika Anda masih belum paham tentang pengujian, Anda bisa cek kembali modul 0 tentang pengujian.

Perhatikan kode berikut:

Kotlin

@get:Rule
var thrown = ExpectedException.none()
...
thrown.expect(NumberFormatException::class.java)
thrown.expectMessage("For input string: \"2.4\"")


Java

@Rule
var thrown = ExpectedException.none()
...
thrown.expect(NumberFormatException.class);
thrown.expectMessage("For input string: \"2.4\"");

Kode di atas berguna untuk memastikan apakah pesan eror yang terjadi sesuai dengan ekspektasi Anda.

Pada kasus di atas, ekspektasinya yaitu variable yang dimasukkan bukan integer, melainkan double. Untuk menggunakan ExpectedException, Anda perlu menggunakan anotasi Rule untuk memberi aturan pada pengujian .

Jika Anda masih belum paham tentang pengujian, Anda bisa cek kembali modul 0 tentang pengujian.

Source code dapat Anda unduh pada tautan berikut: