Mock — apa itu, objek mock dan pustaka untuk pengujian

Penulis: IT Sectr Diterbitkan: 2026-04-10 Waktu membaca: 8 mnt

Mock — adalah objek pengganti yang meniru perilaku komponen nyata dan memungkinkan pemeriksaan interaksi dengannya. Tidak seperti Stub yang hanya mengembalikan nilai yang ditentukan, Mock mencatat fakta pemanggilan metode, argumen yang diberikan, dan jumlah pemanggilan. Menurut data Mockito (2024), Mock adalah jenis Test Double paling populer di proyek Java dan Kotlin, digunakan di lebih dari 70% pengujian unit aplikasi seluler.

Poin Utama

  • Mock — objek yang memeriksa interaksi: metode mana yang dipanggil, dengan argumen apa, dan berapa kali
  • Mockito — pustaka paling populer untuk membuat Mock di proyek Java dan Android
  • MockK — alternatif Mockito untuk Kotlin dengan dukungan native untuk coroutine dan sealed class
  • Behavior verification — perbedaan utama Mock dari Stub: Mock memeriksa perilaku, bukan keadaan
  • Over-mocking — Anti-Pattern utama: mock harus hanya untuk dependensi eksternal

Apa itu Mock?

Mock — adalah objek yang dibuat oleh framework mocking (Mockito, MockK, EasyMock), yang meniru antarmuka atau kelas dan mencatat semua panggilan metodenya. Pengembang menetapkan ekspektasi: metode X akan dipanggil dengan argumen Y dan mengembalikan Z. Setelah pengujian dijalankan, Mock memeriksa apakah ekspektasi sesuai dengan panggilan sebenarnya.

Istilah ini berasal dari metafora teater Test Doubles: Mock adalah “peniru” yang tidak hanya berdiri di atas panggung (seperti Dummy), tetapi memainkan peran dan memverifikasi apakah interaksi dengannya sudah benar. Jika kode yang diuji tidak memanggil metode yang diharapkan Mock, atau memanggilnya dengan argumen yang salah — pengujian gagal dengan pesan tentang ekspektasi yang dilanggar.

Bagaimana Mock bekerja

Mock dibuat melalui pabrik framework: mockk<MyInterface>() atau Mockito.mock(MyClass.java). Framework menghasilkan objek proxy yang mencegat semua panggilan metode. Setiap panggilan dibandingkan dengan ekspektasi (expectations) yang telah ditentukan sebelumnya. Jika panggilan sesuai dengan ekspektasi — nilai yang ditentukan dikembalikan. Jika tidak — Mock mengembalikan nilai default atau melempar pengecualian, tergantung pada konfigurasi.

Kapan Mock diperlukan

Mock wajib digunakan ketika kode yang diuji berinteraksi dengan komponen yang memiliki efek samping: mengirim data ke server, menulis ke basis data, pencatatan log, analytics, navigasi, menampilkan dialog sistem. Tanpa Mock, interaksi ini tidak dapat diverifikasi tanpa menjalankan infrastruktur nyata. Menurut Google Testing Blog, Mock adalah satu-satunya cara untuk memeriksa bahwa aplikasi benar-benar mengirimkan peristiwa analytics tanpa menjalankan server pengujian.

Mock dan Stub: perbandingan detail

Perbedaan antara Mock dan Stub adalah salah satu topik yang paling banyak diperdebatkan dalam pengujian. Kedua jenis menggantikan dependensi nyata, tetapi dengan cara yang mendasar berbeda.

KriteriaMockStub
Pertanyaan utamaApakah metode dipanggil?Hasil apa yang dikembalikan?
VerifikasiPerilaku (verify)Keadaan (assert)
Pengembalian dataOpsionalWajib
Contohverify(analytics).logEvent("click")assertEquals(5, repository.getCount())
Kapan digunakanEfek sampingPengembalian data

Aturan praktis: Mock atau tidak

Uji sederhana untuk memilih: tanyakan pada diri sendiri — “jika saya menghapus baris kode ini, apakah pengujian akan gagal?”. Jika pengujian memeriksa nilai yang dikembalikan — diperlukan Stub (pemeriksaan melalui assert). Jika pengujian memeriksa apakah kode memanggil metode dengan argumen yang benar — diperlukan Mock (pemeriksaan melalui verify). Dikotomi ini mengikuti pola Command-Query Separation: metode yang mengubah keadaan (commands) memerlukan Mock; metode yang mengembalikan data (queries) memerlukan Stub.

Mockito dan MockK: perbandingan pustaka

Pilihan antara Mockito dan MockK adalah salah satu keputusan pertama saat menyiapkan tumpukan pengujian proyek Android di Kotlin. Kedua pustaka melakukan tugas yang sama, tetapi dengan pendekatan berbeda terhadap spesifikasi Kotlin.

Mockito: klasik yang teruji

Mockito — adalah standar de facto untuk proyek Java. Versi 5.x mendukung objek mock untuk kelas final, metode statis, dan konstruktor berkat MockMaker bawaan. Untuk proyek Kotlin, Mockito memerlukan konfigurasi tambahan: ekstensi mockito-kotlin untuk sintaksis yang lebih baik, mockito-inline untuk kelas final. Mockito tidak mendukung coroutine Kotlin dan fungsi suspend tanpa adaptor tambahan.

MockK: pendekatan Kotlin-first

MockK dibuat khusus untuk Kotlin. Ia mendukung secara native coroutine (coEvery, coVerify), sealed class, data class, singleton object, dan fungsi ekstensi. Sintaksis MockK menggunakan DSL dengan blok lambda, yang terlihat alami dalam kode Kotlin. MockK juga dapat melakukan mock properti (property mocking) tanpa konfigurasi tambahan — ini penting untuk proyek Android yang menggunakan LiveData, StateFlow, dan Delegates.

kotlin
// Mockito + mockito-kotlin
val repository = mock<UserRepository>()
whenever(repository.getUser(1)).thenReturn(user)

// MockK
val repository = mockk<UserRepository>()
every { repository.getUser(1) } returns user

Perbandingan kinerja

Tolok ukur (JVM Benchmark, 2024) menunjukkan bahwa MockK membuat objek mock 15–20% lebih cepat daripada Mockito untuk proyek Kotlin berkat kerja langsung dengan bytecode Kotlin, bukan Java Reflections. Untuk proyek dengan ribuan pengujian unit, perbedaan waktu pembangunan bisa terlihat: MockK menghemat 30–60 detik pada proses pengujian penuh di proyek besar.

Contoh pengujian Mock di Kotlin

Kita akan memeriksa tiga skenario: menguji ViewModel dengan dependensi Mock, menguji UseCase dengan verifikasi panggilan API, dan menguji coroutine dengan coVerify.

Contoh 1: ViewModel dengan analytics yang di-mock

kotlin
class ProfileViewModelTest {
    private val analytics = mockk<AnalyticsService>()
    private val repo = mockk<UserRepository>()
    private val vm = ProfileViewModel(repo, analytics)

    fun `profile opened logs analytics event`() {
        every { analytics.logEvent("profile_opened") } returns Unit

        vm.onViewCreated()

        verify { analytics.logEvent("profile_opened") }
    }
}

Contoh 2: UseCase dengan verifikasi asinkron

kotlin
class SendMessageUseCaseTest {
    private val api = mockk<MessagingApi>()
    private val useCase = SendMessageUseCase(api)

    fun `send message with correct payload`() = runTest {
        val message = Message(text = "Hello", userId = 42)

        coEvery { api.sendMessage(any()) } returns MessageResult.Sent("msg_1")

        val result = useCase.execute(message)

        coVerify {
            api.sendMessage(match {
                it.text == "Hello" && it.userId == 42
            })
        }
        assertTrue(result is MessageResult.Sent)
    }
}

Contoh 3: verifikasi argumen dengan ArgumentCaptor

kotlin
class OrderUseCaseTest {
    private val api = mockk<OrderApi>()
    private val useCase = OrderUseCase(api)
    private val slot = slot<OrderRequest>()

    fun `order request contains correct items`() = runTest {
        coEvery { api.placeOrder(capture(slot)) } returns OrderResult.Placed("order_1")

        useCase.execute(listOf("item_a", "item_b"))

        assertEquals(2, slot.captured.items.size)
        assertEquals("item_a", slot.captured.items[0])
    }
}

Praktik terbaik pengujian Mock

Penggunaan Mock yang efektif dalam pengembangan seluler memerlukan disiplin. Pelanggaran aturan ini mengubah pengujian menjadi hambatan rapuh yang rusak pada setiap refactoring.

Mock hanya batas eksternal aplikasi

Aturan ketat: Mock dibuat hanya untuk dependensi yang melintasi batas aplikasi: klien API, basis data, sistem file, layanan sistem (LocationManager, BluetoothAdapter, Camera). Kelas internal aplikasi — entitas domain, Value Object, utilitas sederhana — tidak boleh diganti dengan Mock. Perilakunya diuji melalui objek nyata.

Satu assert/verify per pengujian

Setiap pengujian harus berisi tepat satu pemeriksaan logis — baik verify (untuk Mock), atau assert (untuk Stub). Jangan mencampur verifikasi keadaan dan perilaku dalam satu pengujian. Jika perlu memeriksa baik panggilan API maupun hasilnya — buat dua pengujian terpisah dengan nama berbeda. Aturan ini, dikenal sebagai “satu assert per pengujian”, berasal dari rekomendasi Kent Beck (2002).

  • Gunakan relaxUnitFun = true di MockK untuk metode yang mengembalikan Unit — jika tidak, Mock akan melempar pengecualian pada panggilan yang tidak dijelaskan
  • Batasi verify hanya pada panggilan kritis — jangan verifikasi setiap getter dan setter, ini membuat pengujian rapuh
  • Terapkan ArgumentMatchers secara bermakna — any() menyembunyikan detail penting jika argumen sangat penting untuk logika bisnis
  • Jangan menyalahgunakan verifyNoMoreInteractions — metode ini membuat pengujian menjadi terlalu kaku terhadap perubahan apa pun dalam kode produksi
  • Gunakan @MockkAnnotations untuk inisialisasi otomatis objek Mock — ini mengurangi boilerplate dan meningkatkan keterbacaan

Teknik lanjutan pengujian Mock

Selain mocking dasar, ada teknik lanjutan yang menyelesaikan tugas spesifik dalam pengembangan seluler: menguji multi-threading, memeriksa keadaan Flow, dan mocking parsial objek nyata.

Partial Mock dengan spyK

Spy (atau partial mock) memungkinkan pembuatan objek yang mendelegasikan panggilan ke implementasi nyata, tetapi memungkinkan penimpaan metode tertentu. Di MockK, spyk dibuat berdasarkan instance nyata kelas: val repo = spyk(InMemoryUserRepository()). Panggilan yang ekspektasinya ditetapkan melalui every melewati Mock; sisanya — melalui objek nyata. Spy sangat berguna untuk menguji kode legacy di mana injeksi dependensi belum diterapkan, dan hanya perlu menimpa satu metode.

Menguji StateFlow dengan Turbine

Dalam proyek Android modern dengan Jetpack Compose, ViewModel mengekspos keadaan melalui StateFlow. MockK memungkinkan mocking dependensi Flow, dan pustaka Turbine menyederhanakan verifikasi emisi. Pola klasik: MockK untuk UseCase yang mengembalikan Flow, Turbine untuk memverifikasi emisi ViewModel. Tumpukan ini direkomendasikan oleh dokumentasi Android Testing (Google, 2024) untuk proyek pada Kotlin Coroutines.

kotlin
class SearchViewModelTest {
    private val searchUseCase = mockk<SearchUseCase>()
    private val vm = SearchViewModel(searchUseCase)

    fun `search emits results`() = runTest {
        coEvery { searchUseCase.search("android") } returns
            flowOf(SearchResult.Success(listOf(Item("Android TDD"))))

        vm.search("android")

        vm.state.test {
            val state = awaitItem()
            assertTrue(state.items.isNotEmpty())
            cancelAndIgnoreRemainingEvents()
        }
    }
}

Pertanyaan yang Sering Diajukan

Apa perbedaan Mock dengan Mockito?

Mock — adalah konsep, jenis Test Double yang memeriksa perilaku. Mockito — adalah pustaka untuk membuat objek Mock di Java dan Android. Pustaka lainnya: MockK (Kotlin), EasyMock (Java), Cuckoo (iOS).

Bagaimana Mock bekerja dengan coroutine Kotlin?

Untuk menguji fungsi suspend dengan Mock, gunakan MockK (coEvery / coVerify) atau Mockito dengan mockito-kotlin. MockK mendukung coroutine secara native: coEvery mendefinisikan perilaku fungsi suspend, coVerify memeriksa panggilannya di dalam coroutine. Semua panggilan suspend harus dijalankan di dalam runTest (kotlinx-coroutines-test).

Bisakah Mock mengembalikan nilai yang berbeda pada panggilan berulang?

Ya. Di MockK untuk ini digunakan returnsMany: every { api.getData() } returnsMany listOf(response1, response2). Di Mockito — rantai thenReturn(value1).thenReturn(value2). Ini berguna untuk menguji perilaku pada panggilan berurutan dengan respons berbeda.

Bagaimana cara membersihkan keadaan Mock antar pengujian?

Di MockK gunakan anotasi @MockK dengan bidang relaxed = true dan panggil clearMocks(mock) di metode @After. Di MockitoMockito.reset(mock). Praktik terbaik: buat Mock baru untuk setiap pengujian melalui @Before untuk menghilangkan pengaruh antar pengujian.

Bagaimana Mock menangani sealed class di Kotlin?

MockK bekerja dengan benar dengan sealed class: every { useCase() } returns Result.Success(data). Mockito tidak mendukung sealed class secara langsung, memerlukan solusi alternatif. Ini adalah salah satu alasan mengapa untuk proyek Kotlin, MockK direkomendasikan daripada Mockito.

Ringkasan

  • Mock — jenis Test Double yang memeriksa perilaku (verify), bukan keadaan (assert) dependensi
  • Mockito — standar untuk Java/Android, MockK — pilihan Kotlin-first dengan dukungan coroutine dan sealed class
  • Aturan utama: Mock untuk batas eksternal (jaringan, DB, layanan sistem), objek nyata untuk kelas internal
  • Over-mocking — Anti-Pattern utama: penggantian dependensi yang berlebihan membuat pengujian rapuh dan tidak berguna
  • Satu pengujian — satu pemeriksaan logis: verify untuk Mock atau assert untuk Stub, tetapi tidak keduanya dalam satu pengujian
  • ArgumentCaptor / slot — cara yang benar untuk memeriksa argumen panggilan Mock, bukan any() buta
  • MockK direkomendasikan untuk proyek Kotlin: coEvery dan coVerify bekerja secara native dengan coroutine tanpa adaptor tambahan

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.

Diskusikan proyek

Baca juga