Macam-Macam Toolbox Testing pada Spring Framework

Ichwan Sholihin
7 min readDec 17, 2023
Photo by Alvaro Reyes on Unsplash

Testing (pengujian) adalah salah satu aspek penting dalam pengembangan perangkat lunak. Dengan testing, kita dapat memastikan bahwa aplikasi kita berfungsi sebagaimana mestinya dan menghasilkan output yang diharapkan. Spring Boot menyediakan berbagai macam fitur untuk memudahkan kita dalam melakukan testing. Fitur-fitur ini dapat membantu kita untuk melakukan unit testing, integration testing, dan end-to-end testing. Ketiga jenis testing ini memiliki fokus yang berbeda dalam hal scope dan complexity nya.

Unit testing berfokus pada pengujian unit terkecil dari sebuah kode, biasanya berupa method ataupun class untuk memastikan bahwa unit-unit kode tersebut beroperasi dengan benar sesuai skenario yang diinginkan dan tidak bergantung pada unit kode lainnya. Integration testing berfokus pada pengujian bagaimana unit-unit kode tersebut dapat berinteraksi satu sama lain. Tujuannya adalah untuk memastikan bahwa modul atau komponen yang berbeda itu dapat bekerja sama sebagai satu kesatuan yang komprehensif, mencegah interaksi yang tak terduga serta meningkatkan stabilitas sistem. End-to-end testing berfokus pada pengujian sistem secara keseluruhan dari awal hingga akhir untuk memastikan bahwa aplikasi perangkat lunak secara keseluruhan dapat berfungsi sebagaimana mestinya, dengan meniru interaksi pengguna yang sebenarnya.

Namun pada artikel berikut, kita hanya akan membahas testing tools yg biasa digunakan pada Spring Boot dengan tujuan untuk meningkatkan kualitas dan keandalan aplikasi pada Spring. Berikut beberapa testing tools yang sering digunakan pada framework Spring.

Mock Mvc

Mock MVC adalah testing framework untuk Spring MVC applications. Mock MVC memungkinkan kita untuk menguji controller secara isolate (terpisah) tanpa perlu menjalankan server web yang sebenarnya. Kita dapat mengirim request mock ke controller dan memverifikasi response yang dihasilkan. Mock MVC bekerja dengan cara menciptakan mock object dari controller yang akan diuji. Mock object ini bertindak sebagai pengganti controller yang sebenarnya dengan menginjeksikan mock object ini ke dalam controller yang akan diuji. Ketika mengirim request ke controller, Mock MVC akan mensimulasikan perilaku controller yang sebenarnya. Mock MVC akan memeriksa request yang dikirim dan menentukan bagaimana controller akan meresponsnya.

Mock MVC termasuk dalam kategori integration testing karena menguji interaksi antara controller dan komponen lain di dalam aplikasi, seperti model dan service. Berikut adalah contoh dari MockMvc:

@Slf4j
@SpringBootTest
@AutoConfigureMockMvc
class ServOrderControllerTest {

@Autowired
private MockMvc mockMvc;

@MockBean
private ServOrderService servOrderService;

@ParameterizedTest
@ValueSource(strings = {"CL0001-20231010", "FS0001-20231010", "PL0001-20231010"})
void itShouldPrintServiceOrderById_Success(String id) throws Exception {

ServiceOrders serviceOrders = ServiceOrders.builder()
.orderId(id)
.orderType(EnumModuleServiceOrders.OrderType.CREATE)
.orderStatus(EnumModuleServiceOrders.OrderStatus.OPEN).build();

when(servOrderService.findServiceOrdersById(id)).thenReturn(serviceOrders);

mockMvc.perform(
MockMvcRequestBuilders.get("/search?id={id}", id)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print());

verify(servOrderService, times(1)).findServiceOrdersById(seroId);
}
}

Pada kode diatas, terdapat anotasi @AutoConfigureMockMvc untuk menguji controller. Kemudian menginjeksi instance MockMvc untuk mengirim request dari client dan memverifikasi response berupa JSON. Di dalam method, kita tetap membuat object tiruan dengan mendeklarasikan beberapa field dari entity yang ingin di test lalu menggunakan method Mockito.when() untuk melihat apakah return dari method findServiceOrdersById(id) dari interface service yang di mock di ServOrderService sudah sesuai.

public interface ServOrderService {

ServiceOrders addServiceOrders(Long servId) throws Exception;

//mock object
ServiceOrders findServiceOrdersById(String seroId);

int selectPartner(Partner partner, String seroId);

}

Gunakan mockMvc.perform(…) untuk membangun dan mengirim request GET ke endpoint /search dengan ID test sebagai query parameter. Mockito.verify() digunakan untuk memverifikasi bahwa method findServiceOrdersById pada mock service dipanggil tepat satu kali dengan ID yang benar. Untuk lebih lengkapnya, lihat dokumentasi resmi yang terdapat pada laman berikut:

JUnit

JUnit adalah kerangka kerja pengujian unit untuk Java. JUnit berperan penting dalam pengembangan pengembangan berbasis pengujian, dan merupakan salah satu dari framework unit testing yang secara kolektif dikenal sebagai xUnit yang berasal dari SUnit. JUnit memungkinkan kita untuk menulis test unit pada class dan method di Java. Test unit adalah test yang menguji unit kode individu, seperti class atau method.

Untuk memulai menggunakan JUnit pada Spring, tambahkan terlebih dahulu dependency dari JUnit 5:

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>

Berikut adalah contoh penggunaan JUnit pada Spring:

public class CalculatorService {
public int add(int a, int b) {
return a + b;
}
}

Pada contoh di atas, kita memiliki service CalculatorService yang memiliki method add untuk menambahkan dua bilangan bulat.

@SpringBootTest
public class CalculatorServiceIntegrationTest {

@Autowired
private CalculatorService calculatorService;

@Test
public void testAdd() {
// Eksekusi
int result = calculatorService.add(2, 3);

// Verifikasi
assertEquals(5, result, "Penambahan harus benar");
}
}

Lalu service CalculatorService diinject ke dalam spring testing menggunakan anotasi @Autowired. Pengujian ini memastikan bahwa service berinteraksi dengan dependensinya (dalam hal ini, service CalculatorService) dengan benar. Untuk dokumentasi lengkapnya ada di laman berikut:

AssertJ

AssertJ merupakan salah satu library unit test di Java yang menyediakan kumpulan assertions dan throwable dengan tujuan untuk meningkatkan kualitas readable dari unit test. Hampir sama dengan JUnit namun AssertJ menyediakan kumpulan assertions yang lebih banyak dan intuitif untuk digunakan dalam unit testing, sedangkan JUnit adalah framework unit testing yang menyediakan infrastruktur untuk menjalankan unit test serta menggunakan sintaks yang lebih sederhana.

Untuk menggunakan AssertJ, tambahkan sebuah dependency pada file pom.xml

<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
</dependency>

Berikut gambaran dari penggunaan AssertJ:

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class MyClassTest {

@Test
public void testSomething() {
// Assert that the value of the variable is equal to 10
assertThat(variable).isEqualTo(10);

// Assert that the object is not null
assertThat(object).isNotNull();

// Assert that the list contains the element "foo"
assertThat(list).contains("foo");
}
}

AssertJ juga menyediakan berbagai macam assertions untuk memeriksa kondisi dan nilai yang lebih kompleks. Misalnya, kita dapat menggunakan pernyataan assersi AssertJ untuk memeriksa apakah objek memiliki properti tertentu, apakah objek memenuhi kriteria tertentu, atau apakah objek dalam keadaan tertentu. Untuk penjelasan lebih lengkap, kunjungi laman berikut:

Hamcrest

Hamcrest adalah assertion framework yang menyediakan serangkaian matcher untuk menguji kondisi dan nilai dari objek atau variabel. Matcher adalah objek yang digunakan untuk memeriksa apakah nilai dari objek atau variabel memenuhi kriteria tertentu. Untuk menggunakan Hamcrest dalam unit test, kita perlu menambahkan dependensi Hamcrest kemudian dapat menggunakan matcher Hamcrest di dalam unit testing.

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>

Berikut contoh penerapan Hamcrest pada unit testing:

import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;

public class MyClassTest {

@Test
public void testSomething() {
// Assert that the value of the variable is equal to 10
MatcherAssert.assertThat(variable, Matchers.is(10));

// Assert that the object is not null
MatcherAssert.assertThat(object, Matchers.notNullValue());

// Assert that the list contains the element "foo"
MatcherAssert.assertThat(list, Matchers.contains("foo"));
}
}

Saat ini, Hamcrest dapat digunakan pada beberapa bahasa pemrograman seperti JavaScript, Python, C# dan lain-lain. Hamcrest juga menyediakan berbagai macam matcher untuk memeriksa kondisi dan nilai yang lebih kompleks. Untuk lebih lengkapnya, cek dokumentasi resmi Hamcrest pada laman berikut:

JSONassert

JSONAssert adalah library Java yang memfasilitasi pengujian struktur dan isi dari data JSON dalam unit testing. Ia menawarkan cara yang lebih baik dan lebih mudah dibaca dibandingkan dengan menulis assertions secara manual untuk setiap elemen individual dalam struktur JSON. JSONassert melakukan pemeriksaan mendalam terhadap struktur dan isi JSON, seperti memverifikasi keberadaan key tertentu, value tertentu, dan bahkan urutan kemunculan elemen. Untuk mengimplementasikan JSONassert, tambahkan dependency berikut:

<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.5.1</version>
</dependency>

Berikut adalah contoh penerapan JSONassert pada unit testing:

import static org.assertj.core.api.Assertions.assertThat;

String actualJson = "{\"name\":\"John\",\"age\":30}";
String expectedJson = "{\"name\":\"John\",\"age\":30}";

assertThat(actualJson).isEqualTo(expectedJson); // Lolos, struktur dan isi identik

actualJson = "{\"name\":\"John\",\"age\":30}";
expectedJson = "{\"age\":30,\"name\":\"John\"}";

assertThat(actualJson).usingRecursiveComparison().isEqualTo(expectedJson); // Lolos, urutan elemen berbeda tapi isinya sama

actualJson = "{\"name\":\"John\",\"age\":30}";
expectedJson = "{\"name\":\"John\",\"age\":25}";

assertThat(actualJson).isEqualTo(expectedJson); // Gagal, nilai "age" tidak sesuai

JSONAssert menggunakan sintaks yang mengalir dan mudah dipahami, seperti assertThat(jsonString).isEqualTo(expectedJsonString), untuk membandingkan struktur dan isi dari data JSON. Untuk lebih lengkapnya terdapat pada dokumentasi berikut:

JsonPath

JsonPath adalah library API untuk memanipulasi data JSON. Ia dapat digunakan untuk mengakses, membaca, dan menulis data JSON, serta untuk melakukan berbagai operasi pada data JSON, seperti pemfilteran, sorting, dan agregasi. JsonPath menyediakan berbagai macam fungsi untuk mengakses dan memanipulasi data JSON. Misalnya, kita dapat menggunakan JsonPath untuk mengakses nilai dari elemen dalam array, memfilter data berdasarkan kriteria tertentu, atau mengurutkan data. Berikut dependency yang harus ditambahkan untuk penerapannya di unit testing:

<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.8.0</version>
</dependency>

Dan dibawah ini adalah contoh penerapan dari JsonPath:

import com.jayway.jsonpath.JsonPath;

String json = "{\"name\":\"John\",\"age\":30}";

// Mengakses nilai dari kunci "name"
String name = JsonPath.read("$.name", json);
System.out.println(name); // John

// Mengakses nilai dari elemen dalam array
String[] names = JsonPath.read("$.names", json, String[].class);
System.out.println(names); // [John]

// Memfilter data berdasarkan kriteria tertentu
String filteredJson = JsonPath.parse(json).filter("$.age > 25").jsonString();
System.out.println(filteredJson); // {"name":"John","age":30}

// Mengurutkan data
String sortedJson = JsonPath.parse(json).sort("$.age").jsonString();
System.out.println(sortedJson); // {"age":25,"name":"John"}

JsonPath expressions selalu mengacu pada struktur JSON dengan cara yang sama seperti XPath expression yang digunakan dalam kombinasi dengan dokumen XML. “root member object” di JsonPath selalu ditulis dengan $ terlepas dari apakah itu object atau array. Untuk dokumentasi dan contoh penerapan secara detail dapat dilihat pada repository berikut:

Ada banyak tools dalam testing terutama pada spring framework seperti awaitility, selenium, MockWebServer (dari OkHttp), TestContainers, WireMock, Selendine dan lain-lain sesuai kebutuhan dan kasus penerapannya.

Untuk refactoring code secara otomatis, kita juga dapat menggunakan OpenRewrite, salah satu framework pada Java untuk refactoring dan API migrations.

--

--