Tulisan kali ini akan melanjutkan pembahasan mengenai sebuah teknik pengujian pada perangkat lunak yang disebut unit testing yang pernah saya kemukakan disini. Pengujian jenis ini dilakukan dengan cara terisolasi, maksudnya tidak boleh ada interaksi langsung dengan kelas lain yang menjadi collaborator kelas tersebut.
Kenyataanya sebuah kelas berinteraksi dengan kelas lain untuk melakukan sebuah fungsionalitas meskipun kadang juga ada sebuah kelas yang bisa berdiri sendiri tanpa bantuan dari kelas collaborator. Salah satu contoh kode kelas yang berinteraksi dengan kelas lain adalah kelas BookService
1 2 3 4 5 6 7 |
public class BookService { BookDao bookDao; ..... ..... ..... } |
Dari potongan kode ditunjukkan bahwa BookDao merupakan sebuah collaborator dari kelas BookService. Kelas diatas membutuhkan sebuah dao untuk berinteraksi dengan basisdata. Jika menggunakan cara “seperti biasa” maka untuk mengetes kelas BookService kita akan membutuhkan sebuah koneksi dengan basisdata karena didalam kelas tersebut terdapat peran dari BookDao. Tentunya kita juga harus mengisi database dengan beberapa data contoh untuk pengujian. Pengujian seperti itu dinamakan integration testing bukan unit testing. Pengujian unit hanya fokus pada pengujian sebuah method pada kelas, tidak bagaimana dia berinteraksi dengan kelas atau sistem lain.
Sekarang bagaimana caranya supaya pengujian terhadap kelas BookService bisa berjalan secara terisolasi padahal terdapat hubungan dengan BookDao ?. Solusinya adalah membuat sebuah mock object atau objek palsu untuk simulasi dari sebuah BookDao.
Pengujian menggunakan Mock object memiliki 2 alasan:untuk mengisolasi kelas yang sedang diuji agar tidak terpengaruh dari luar dan untuk mengontrol ketergantungan. Seperti sudah saya katakan diatas bahwa pengujian unit dilakukan secara terisolasi, tetapi jarang ditemukan sebuah kelas yang tidak memeliki ketergantungan (dependency) dengan kelas lain. Jika dependensi yang ada merupakan sebuah kelas yang simpel misal POJO ataupun collection maka cukup dibuat instance-nya saja menggunakan operator new. Lain halnya jika kelas yang menjadi dependensi merupakan sebuah kelas yang bisa dikatakan “berat”, misalnya bergantung dengan kelas lain lagi, kemudian kelas lain tersebut bergantung dengan kelas lain lagi begitu seterusnya atau bergantung dengan sebuah database. Dengan menggunakan mock object, kelas yang menjadi ketergantungan dari kelas yang sedang diuji bisa kita kendalikan sesuka hati menyesuaikan kebutuhan pengujian. Jika menggunakan objek asli melalui operator new, pengendalian akan susah dilakukan.
Supaya jelas saya kasih gambaran seperti ini. Ada kelas BookService yang memiliki method untuk men-generate sebuah kode secara urut. Jika kode sebelumnya adalah “BK-0025” maka kode selanjutnya adalah “BK-0026”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package com.agungsetiawan.autonumbermock.service; import com.agungsetiawan.autonumbermock.dao.BookDao; public class BookService { BookDao bookDao; public BookService(BookDao bookDao){ this.bookDao=bookDao; } public String nextNumber(){ String lastNumber=bookDao.lastNumber(); String finalNumber; if(lastNumber==null){ finalNumber="BK-0001"; }else{ String[] numberOnly=lastNumber.split("-"); int numberNow=Integer.parseInt(numberOnly[1])+1; finalNumber="BK-"+String.format("%04d", numberNow); } return finalNumber; } } |
Kelas diatas hanya bertanggung jawab untuk men-generate kode selanjutnya. Dia tidak bertanggung jawab untuk mendapatkan kode terakhir yang ada pada database. Tugas tersebut dihandle oleh BookDao, jadi sekarang jelas dimana letak dependensinya.
Untuk mengeuji kelas diatas maka kita harus membuat sebuah mock object dari BookDao. Timbul pertanyaan, jika dao yang digunakan hanyalah objek palsu/objek simulasi maka bagaimana dia bisa mendapatkan kode terkahir yang ada pada database?. Jawabannya adalah kita yang menentukan sendiri kode terakhir yang ingin kita peroleh untuk menguji kelas BookService. Jadi tidak perlu terhubung ke database untuk mendapatkan kode terakhir.
Ada beberapa library yang bisa digunakan untuk membuat sebuah mock object, diantaranya adalah Mockito.Berikut adalah deklarasainya pada file pom.xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.8.4</version> </dependency> </dependencies> |
Kode pengujiannya ditunjukkan dibawah ini
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package com.agungsetiawan.autonumbermock.service; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import com.agungsetiawan.autonumbermock.dao.BookDao; public class BookServiceTest { BookDao bookDao; BookService bookService; @Before public void setUp(){ bookDao=Mockito.mock(BookDao.class); bookService=new BookService(bookDao); } @Test public void nextNumberTest(){ Mockito.when(bookDao.lastNumber()).thenReturn("BK-0123"); String actual=bookService.nextNumber(); Assert.assertEquals("BK-0124", actual); } @Test public void nextNumberFromZeroTest(){ Mockito.when(bookDao.lastNumber()).thenReturn(null); String actual=bookService.nextNumber(); Assert.assertEquals("BK-0001", actual); } } |
Pada baris yang saya tandai yaitu method when dan thenReturn, method inilah yang digunakan untuk proses simulasi. Maksud dari Mockito.when(bookDao.lastNumber()).thenReturn(“BK-0123”); adalah, ketika bookDao.lastNumber() dieksekusi maka nilai kembaliannya adalah “BK-0123”. Mudah bukan? tidak perlu terkoneksi dengan database untuk menguji kelas service diatas.
Oh iya sebagai informasi, untuk sebuah kelas agar bisa dilakukan pengujian unit terhadap dirinya maka diperlukan beberapa syarat yang harus dipenuhi. Hal ini akan saya bahas pada tulisan selanjutnya.
Semoga bermanfaat 🙂
Referensi
1. Ladd, Seth and Keith Donald, Expert Spring MVC and Web Flows, Apress, 2006