Episode ini mengajarkan jaminan kualitas: unit testing dengan JUnit 5 dan Mockito, integration testing dengan @SpringBootTest dan @WebMvcTest, contract testing dengan Spring Cloud Contract, serta Testcontainers untuk database dan service dependencies.

Kode tanpa test adalah kode yang belum selesai. Episode 17 membahas testing dan quality assurance di Spring Boot — dari unit test yang cepat hingga integration test yang realistis dengan container sungguhan.
Kalian akan belajar menulis unit test dengan JUnit 5 dan Mockito, memakai slice test seperti @WebMvcTest, menguji interaksi antar service dengan Testcontainers, dan memahami contract testing untuk microservices.
Spring Boot menyediakan starter test yang membundel JUnit 5, Mockito, AssertJ, dan lain-lain:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>Unit test fokus pada satu class; dependency diganti mock. Contoh menguji service yang bergantung pada repository:
@ExtendWith(MockitoExtension.class)
class ItemServiceTest {
@Mock
private ItemRepository repository;
@InjectMocks
private ItemService service;
@Test
void cariItemBerdasarkanId() {
Item item = new Item(1L, "Laptop");
when(repository.findById(1L)).thenReturn(Optional.of(item));
Item hasil = service.findById(1L);
assertThat(hasil.getName()).isEqualTo("Laptop");
verify(repository).findById(1L);
}
}when(...).thenReturn(...) menyetel perilaku mock, dan verify(...) memastikan method benar-benar dipanggil. Unit test ini berjalan dalam milidetik tanpa database.
Untuk memastikan semua bean terpasang dengan benar, gunakan @SpringBootTest — test memuat seluruh application context:
@SpringBootTest
@AutoConfigureMockMvc
class ItemControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void listItemsMengembalikanStatusOk() throws Exception {
mockMvc.perform(get("/api/items"))
.andExpect(status().isOk());
}
}MockMvc mengirim request HTTP palsu tanpa memulai server sungguhan — cepat dan deterministik. Test ini memverifikasi bahwa controller, service, dan bean lain terhubung dengan benar.
Untuk menguji controller secara terisolasi tanpa memuat seluruh context, gunakan @WebMvcTest. Hanya lapisan web yang dimuat, dependency lain di-mock:
@WebMvcTest(ItemController.class)
class ItemControllerSliceTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private ItemService service;
@Test
void getItemMengembalikan404() throws Exception {
when(service.findById(99L))
.thenThrow(new ItemNotFoundException(99L));
mockMvc.perform(get("/api/items/99"))
.andExpect(status().isNotFound());
}
}@WebMvcTest jauh lebih cepat daripada @SpringBootTest karena hanya menyiapkan lapisan web. Test ini membuktikan bahwa perilaku HTTP — termasuk error handling dari episode 7 — bekerja dengan benar.
Database in-memory seperti H2 sering berbeda perilakunya dari PostgreSQL. Testcontainers menjalankan container Docker sungguhan selama test, sehingga test berjalan di environment yang sama dengan production:
@Testcontainers
@SpringBootTest
class ItemRepositoryTest {
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:16")
.withDatabaseName("belajar_test");
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
private ItemRepository repository;
// test query sungguhan di PostgreSQL
}Container PostgreSQL dimulai sebelum test dan dihentikan setelahnya. Property datasource di-arahkan ke container lewat @DynamicPropertySource. Pastikan Docker berjalan di mesin development kalian.
Testcontainers juga menyediakan module untuk Redis, Kafka, Elasticsearch, dan lainnya. Pola yang sama — deklarasikan @Container, arahkan property, jalankan test — berlaku untuk semua service dependency.
Dalam microservices, perubahan tak terduga pada satu service bisa merusak service lain. Contract testing memastikan kedua sisi mematuhi kontrak yang disepakati. Dengan Spring Cloud Contract, kontrak ditulis di sisi producer dalam Groovy DSL — misalnya request GET /api/items/1 diharapkan membalas status 200 dengan body berisi id dan name.
Kontrak ini menghasilkan test di sisi producer dan stub di sisi consumer. Setiap perubahan kontrak akan langsung terdeteksi saat build — sebelum merusak integrasi di runtime.
Sebuah strategi yang baik menggabungkan semua level:
./mvnw testPerintah ./mvnw test menjalankan seluruh suite. Di CI/CD (episode 21), perintah ini menjadi gerbang sebelum aplikasi di-deploy.
Episode 17 membekali kalian dengan jaminan kualitas: unit testing dengan JUnit 5 dan Mockito, integration testing dengan @SpringBootTest dan @WebMvcTest, contract testing dengan Spring Cloud Contract, serta Testcontainers untuk menguji database dan dependency sungguhan.
Inti yang harus dibawa pulang:
spring-boot-starter-test menyediakan JUnit 5, Mockito, dan AssertJ.@SpringBootTest memuat seluruh context; @WebMvcTest hanya lapisan web.Di episode 18 selanjutnya kita akan membahas modular architecture dan patterns — arsitektur clean, hexagonal, dan onion; struktur modul fitur dan package-by-feature; design patterns populer; serta event-driven architecture dengan Spring Events dan Kafka.