Test Doubles — adalah objek pengganti yang digunakan dalam pengujian unit sebagai pengganti ketergantungan nyata. Istilah ini diperkenalkan oleh Gerard Meszaros dalam buku “xUnit Test Patterns” (2007) sebagai konsep umum untuk Mock, Stub, Fake, Spy dan Dummy. Menurut Martin Fowler (2024), Test Doubles memungkinkan isolasi komponen yang diuji dari lingkungannya, membuat pengujian menjadi deterministik, cepat, dan independen dari layanan eksternal.
Utama
Test Doubles — adalah istilah dari industri otomotif (pemeran pengganti, “double” untuk aktor) yang dibawa ke pengembangan perangkat lunak. Seperti seorang pemeran pengganti menggantikan aktor dalam adegan berbahaya, Test Double menggantikan komponen nyata dalam skenario pengujian. Ini diperlukan ketika ketergantungan nyata tidak tersedia, lambat, non-deterministik, atau memiliki efek samping.
Konsep Test Double mencakup lima jenis spesifik, masing-masing menyelesaikan tugasnya sendiri. Tipologi Meszaros bersifat kanonik dan digunakan di semua panduan pengujian modern. Perbedaan antara jenis terletak pada tingkat kontrol dan verifikasi: dari pengisian parameter sederhana (Dummy) hingga pemeriksaan lengkap urutan pemanggilan (Mock).
Tujuan utama Test Doubles adalah isolasi modul yang diuji. Dalam pengembangan mobile, ketergantungan nyata adalah server API, database, sistem file, sensor perangkat, layanan sistem (LocationManager, Camera, Bluetooth). Penggunaan langsung komponen-komponen ini membuat pengujian lambat, rapuh, dan tergantung pada lingkungan. Menurut Google Testing Blog (2023), pengujian unit yang terisolasi dengan baik dijalankan dalam milidetik, dan pengujian integrasi dalam hitungan detik dan menit.
Klasifikasi Gerard Meszaros mencakup lima jenis Test Doubles, yang berbeda dalam perilaku dan tujuan penggunaan. Memahami perbedaan di antara mereka adalah dasar dari pengujian unit yang benar.
Dummy — adalah objek yang diteruskan ke metode yang diuji, tetapi tidak pernah digunakan. Dummy hanya diperlukan untuk memenuhi tanda tangan metode. Di Kotlin, ini sering berupa null, emptyList() atau objek dengan pengganti. Dummy tidak boleh mengandung logika apa pun — jika dipanggil, pengujian harus gagal.
Fake — adalah implementasi yang disederhanakan namun berfungsi dari sebuah antarmuka. Tidak seperti Mock dan Stub, Fake berisi logika bisnis nyata, tetapi dalam bentuk yang disederhanakan. Contoh klasik — InMemoryUserRepository, yang menyimpan data di HashMap, bukan database. Fake digunakan ketika logika yang bergantung pada status perlu diuji, tetapi tanpa overhead infrastruktur nyata.
| Jenis | Tujuan | Contoh |
|---|---|---|
| Dummy | Mengisi parameter | null, objek kosong |
| Fake | Implementasi kerja yang disederhanakan | InMemoryRepository |
| Stub | Mengembalikan nilai tetap | when(api.getUser()).thenReturn(user) |
| Spy | Mencatat pemanggilan untuk pemeriksaan | verify(spy).save(user) |
| Mock | Memeriksa interaksi | verify(mock).sendEmail(email) |
Stub mengembalikan nilai yang telah ditentukan untuk pemanggilan tertentu. Stub tidak memeriksa apakah ia dipanggil — ia hanya menyediakan data. Di Mockito, Stub dibuat melalui when(method).thenReturn(value). Stub ideal untuk pengujian ketika ketergantungan harus mengembalikan nilai tertentu, tetapi fakta pemanggilan itu sendiri tidak penting.
Spy — adalah pembungkus di sekitar objek nyata yang mencatat semua pemanggilan untuk verifikasi selanjutnya. Tidak seperti Mock, Spy mendelegasikan pemanggilan ke objek nyata, tetapi memungkinkan pemeriksaan bahwa pemanggilan tersebut terjadi. Di Mockito, Spy dibuat melalui spy(realObject). Spy berguna untuk mocking parsial ketika Anda ingin menggunakan objek nyata tetapi memeriksa beberapa pemanggilan.
Mock — adalah objek dengan ekspektasi pemanggilan yang telah ditentukan. Mock memeriksa apakah metode tertentu dipanggil dengan argumen tertentu dan dalam urutan tertentu. Tidak seperti Stub, Mock berfokus pada verifikasi perilaku, bukan pada pengembalian data. Mock adalah jenis Test Double yang paling kuat dan paling sering digunakan dalam pengembangan mobile.
Perbedaan antara Mock dan Stub sering menyebabkan kebingungan bahkan di kalangan pengembang berpengalaman. Perbedaan utama terletak pada tujuan: Stub memeriksa status (state verification), Mock memeriksa perilaku (behavior verification).
Stub menjawab pertanyaan: “apakah kode mengembalikan hasil yang benar?”. Mock menjawab pertanyaan: “apakah kode memanggil metode yang benar dengan argumen yang benar?”. Dalam pengembangan mobile, Stub digunakan ketika hasilnya penting (misalnya, data dari repositori), dan Mock ketika efek sampingnya penting (misalnya, mengirim email, menulis ke database).
// Stub: pemeriksaan status
every { repository.getUsers() } returns listOf(user)
val result = useCase.getUsers()
assertEquals(1, result.size)
// Mock: pemeriksaan perilaku
every { analytics.logEvent("purchase") } returns Unit
useCase.purchase(item)
verify { analytics.logEvent("purchase") }
Contoh praktis dari semua lima jenis Test Doubles di Kotlin menggunakan MockK — pustaka mocking paling populer untuk proyek Android.
class InMemoryUserRepository : UserRepository {
private val store = mutableMapOf<String, User>()
override fun save(user: User) {
store[user.email] = user
}
override fun findByEmail(email: String): User? {
return store[email]
}
}
class RegisterUseCaseTest {
private val api = mockk<AuthApi>()
private val repo = spyk(InMemoryUserRepository())
private val useCase = RegisterUseCase(api, repo)
fun `register user successfully`() = runTest {
// Stub: mengembalikan respons API tetap
coEvery { api.register("test@test.com") } returns AuthResult.Success("token123")
val result = useCase.execute("test@test.com")
// Verify: memeriksa bahwa pengguna telah disimpan
verify { repo.save(any()) }
assertTrue(result.isSuccess())
}
}
data class Logger(val appContext: Context, val format: FormatType)
fun `test logger with dummy context`() {
// Dummy: Context tidak digunakan di dalam Logger
val dummyContext = mockk<Context>()
val logger = Logger(dummyContext, FormatType.JSON)
assertEquals(FormatType.JSON, logger.format)
}
Pemilihan jenis Test Double tergantung pada apa yang tepat diuji: status, perilaku, atau integrasi. Dalam pengembangan mobile di Android dan iOS, rekomendasi berikut telah terbentuk.
Saat menguji ViewModel, gunakan Mock untuk ketergantungan yang menghasilkan efek samping (repositori, analytics, navigasi) dan Stub untuk ketergantungan yang mengembalikan data (klien API, ContentProvider). Ini memungkinkan pemeriksaan bahwa ViewModel menangani skenario sukses dan error dengan benar.
Di tingkat Repository, Fake (implementasi database in-memory) dan Stub (respons API tetap) lebih disukai. Fake memungkinkan pengujian logika caching dan mode offline tanpa konfigurasi SQLite. Stub mensimulasikan berbagai kode HTTP: 200, 404, 500, timeout.
Penggunaan Test Doubles yang tidak tepat — salah satu penyebab paling umum dari pengujian rapuh yang rusak pada setiap refactoring.
Kesalahan paling umum — melakukan mocking pada segalanya. Jika setiap ketergantungan dalam pengujian digantikan oleh Mock, pengujian berhenti memeriksa perilaku nyata. Mock harus hanya untuk ketergantungan eksternal (jaringan, database, sistem file, layanan sistem). Komponen internal aplikasi (Value Object, data class, utilitas sederhana) tidak boleh diganti.
Kesalahan kedua — membuat Mock tanpa menentukan ekspektasi. Jika metode dipanggil tanpa every / when, Mock mengembalikan nilai default (null, 0, false). Ini dapat menyebabkan pengujian positif palsu ketika Mock diam-diam mengembalikan null, dan pengujian menafsirkannya sebagai perilaku yang benar.
Kesalahan ketiga — memeriksa setiap pemanggilan setiap Mock. Verify harus digunakan hanya untuk pemanggilan yang kritis dari sudut pandang logika bisnis. Verifikasi berlebihan membuat pengujian rapuh: perubahan urutan pemanggilan dalam kode produksi merusak pengujian tanpa mengubah perilaku.
Pertanyaan yang Sering Diajukan
Stub mengembalikan data dan memeriksa status (apa yang dikembalikan), sedangkan Mock memeriksa perilaku (metode apa yang dipanggil). Stub = “kembalikan X”, Mock = “periksa bahwa Y dipanggil dengan argumen Z”. Dalam pengujian nyata, satu objek sering bertindak sebagai Stub dan Mock secara bersamaan.
Fake lebih diutamakan daripada Mock ketika menguji logika yang bergantung pada status: caching, mode offline, transaksi. Fake (implementasi in-memory) memungkinkan pengujian skenario ini tanpa pemanggilan verify yang rapuh. Mock lebih cocok untuk memeriksa pengiriman data: analytics, notifikasi push, email.
Untuk proyek Android di Kotlin, MockK direkomendasikan. Ia mendukung coroutine, fungsi suspend, sealed class dan fungsi extension tanpa konfigurasi tambahan. Untuk proyek Java, standarnya masih Mockito — pustaka paling populer dengan dokumentasi yang luas.
Untuk menguji Kotlin Flow, gunakan pustaka Turbine bersama dengan MockK. Turbine menyederhanakan pemeriksaan emisi Flow: Anda dapat memeriksa urutan nilai, penyelesaian aliran, dan pengecualian. Stub untuk Flow mengembalikan flowOf(value), Mock memeriksa apakah Flow telah dikumpulkan.
Ya, tetapi pada tingkat respons API, bukan komponen UI. Pustaka MockWebServer (OkHttp) dan WireMock memungkinkan penggantian respons HTTP dalam pengujian UI. Komponen UI itu sendiri (Compose, SwiftUI Views) tidak boleh diganti — perilakunya diuji melalui pengujian screenshot dan Espresso.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga