Mock — nedir, mock nesneleri ve test kütüphaneleri

Yazar: IT Sectr Yayınlanma: 2026-04-10 Okuma süresi: 8 dk

Mock, gerçek bir bileşenin davranışını taklit eden ve onunla etkileşimleri doğrulamaya olanak tanıyan bir yedek nesnedir. Yalnızca önceden belirlenmiş bir değer döndüren Stub'ın aksine, Mock, yöntem çağrısının gerçekleştiğini, iletilen argümanları ve çağrı sayısını kaydeder. Mockito (2024)'ye göre, Mock, Java ve Kotlin projelerinde en popüler Test Double türüdür ve mobil uygulamaların birim testlerinin %70'inden fazlasında kullanılır.

Ana Noktalar

  • Mock — etkileşimi doğrulayan nesne: hangi yöntemlerin, hangi argümanlarla ve kaç kez çağrıldığı
  • Mockito — Java ve Android projelerinde Mock oluşturmak için en popüler kütüphane
  • MockK — Kotlin için Mockito alternatifi, coroutine ve sealed class desteği ile
  • Behavior verification — Mock ve Stub arasındaki temel fark: Mock davranışı doğrular, durumu değil
  • Over-mocking — ana anti-pattern: mock yalnızca harici bağımlılıklar için kullanılmalıdır

Mock nedir?

Mock, bir mocking framework'ü (Mockito, MockK, EasyMock) tarafından oluşturulan, bir arayüz veya sınıfı simüle eden ve yöntemlerine yapılan tüm çağrıları kaydeden bir nesnedir. Geliştirici beklentileri belirler: X yöntemi Y argümanlarıyla çağrılacak ve Z döndürecek. Test yürütüldükten sonra Mock, beklentilerin gerçek çağrılarla eşleşip eşleşmediğini doğrular.

Terim, Test Double'ların tiyatro metaforundan gelir: Mock, (Dummy gibi) yalnızca sahnede durmayan, bir rol oynayan ve onunla etkileşimin doğru olup olmadığını doğrulayan bir “taklitçidir”. Test edilen kod, Mock'un beklediği yöntemi çağırmazsa veya yanlış argümanlarla çağırırsa — test, ihlal edilen beklenti mesajıyla başarısız olur.

Mock nasıl çalışır

Mock, framework fabrikası aracılığıyla oluşturulur: mockk<MyInterface>() veya Mockito.mock(MyClass.java). Framework, tüm yöntem çağrılarını yakalayan bir proxy nesnesi oluşturur. Her çağrı, önceden tanımlanmış beklentilerle karşılaştırılır. Çağrı bir beklentiyle eşleşirse — belirtilen değer döndürülür. Eşleşmezse — Mock, yapılandırmaya bağlı olarak varsayılan bir değer döndürür veya bir istisna fırlatır.

Mock ne zaman gereklidir

Mock, test edilen kodun yan etkileri olan bileşenlerle (sunucuya veri gönderme, veritabanına yazma, günlükleme, analiz, gezinme, sistem diyaloglarını gösterme) etkileşime girmesi durumunda zorunludur. Mock olmadan, bu etkileşimler gerçek altyapı çalıştırılmadan doğrulanamaz. Google Testing Blog'a göre Mock, bir test sunucusu başlatmadan bir uygulamanın gerçekten bir analiz olayı gönderip göndermediğini doğrulamanın tek yoludur.

Mock vs Stub: detaylı karşılaştırma

Mock ve Stub arasındaki fark, testlerde en çok tartışılan konulardan biridir. Her iki tür de gerçek bir bağımlılığı değiştirir, ancak temelde farklı şekillerde.

KriterMockStub
Temel soruYöntem çağrıldı mı?Hangi sonuç döndürüldü?
DoğrulamaDavranış (verify)Durum (assert)
Veri dönüşüİsteğe bağlıZorunlu
Örnekverify(analytics).logEvent(“click”)assertEquals(5, repository.getCount())
Ne zaman kullanılırYan etkilerVeri dönüşü

Pratik kural: Mock mu değil mi

Karar vermek için basit bir test: kendinize sorun “bu kod satırını silersem test başarısız olur mu?” Test bir dönüş değerini kontrol ediyorsa — Stub'a (assert tabanlı doğrulama) ihtiyacınız var. Test, kodun doğru argümanlarla bir yöntemi çağırıp çağırmadığını kontrol ediyorsa — Mock'a (verify tabanlı doğrulama) ihtiyacınız var. Bu ikilik, Command-Query Separation modelini izler: durumu değiştiren yöntemler (commands) Mock'a ihtiyaç duyar; veri döndüren yöntemler (queries) Stub'a ihtiyaç duyar.

Mockito vs MockK: kütüphane karşılaştırması

Mockito ve MockK arasında seçim yapmak, Kotlin'de bir Android projesinin test yığınını kurarken ilk kararlardan biridir. Her iki kütüphane de aynı amaca hizmet eder, ancak Kotlin'e özgü özelliklere farklı yaklaşımlarla.

Mockito: kanıtlanmış klasik

Mockito, Java projeleri için fiili standarttır. Sürüm 5.x, yerleşik MockMaker sayesinde final sınıflar, statik yöntemler ve yapıcılar için mocking'i destekler. Kotlin projeleri için Mockito ek kurulum gerektirir: gelişmiş sözdizimi için mockito-kotlin uzantıları, final sınıflar için mockito-inline. Mockito, ek adaptörler olmadan Kotlin coroutine'lerini ve askıya alma işlevlerini desteklemez.

MockK: Kotlin-ilk yaklaşım

MockK özellikle Kotlin için oluşturulmuştur. Coroutine'leri (coEvery, coVerify), sealed class'ları, data class'ları, nesne singleton'larını ve extension işlevlerini yerel olarak destekler. MockK sözdizimi, lambda bloklarıyla DSL kullanır ve Kotlin kodunda doğal görünür. MockK ayrıca ek kurulum olmadan özellik mocking'ini de destekler — bu, LiveData, StateFlow ve Delegates kullanan Android projeleri için önemlidir.

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

Performans karşılaştırması

Karşılaştırmalar (JVM Benchmark, 2024), MockK'nın Java Reflections yerine doğrudan Kotlin bayt koduyla çalışması sayesinde Kotlin projeleri için Mockito'dan %15–20 daha hızlı mock nesneleri oluşturduğunu göstermektedir. Binlerce birim testi olan projelerde, derleme hızındaki fark fark edilebilir: MockK, büyük projelerde tam test çalıştırmasında 30–60 saniye kazandırır.

Kotlin'de Mock test örnekleri

Üç senaryoyu inceleyelim: Mock bağımlılıklarıyla ViewModel testi, API çağrısı doğrulamalı UseCase testi ve coVerify ile coroutine testi.

Örnek 1: Mock analytics ile ViewModel

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") }
    }
}

Örnek 2: Async doğrulama ile UseCase

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)
    }
}

Örnek 3: ArgumentCaptor ile argüman doğrulama

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])
    }
}

Mock testi için en iyi uygulamalar

Mobil geliştirmede Mock'un etkili kullanımı disiplin gerektirir. Bu kuralları ihlal etmek testleri, her yeniden düzenlemede kırılan kırılgan engellere dönüştürür.

Yalnızca harici uygulama sınırlarını Mock'la

Sert bir kural: Mock'lar yalnızca uygulama sınırını aşan bağımlılıklar için oluşturulmalıdır: API istemcileri, veritabanları, dosya sistemi, sistem hizmetleri (LocationManager, BluetoothAdapter, Camera). Dahili uygulama sınıfları — alan varlıkları, Value Objects, basit yardımcı programlar — Mock ile değiştirilmemelidir. Davranışları gerçek nesneler aracılığıyla test edilir.

Test başına bir assert / verify

Her test tam olarak bir mantıksal kontrol içermelidir — verify (Mock için) veya assert (Stub için). Aynı testte durum ve davranış doğrulamasını karıştırmayın. Hem bir API çağrısını hem de sonucunu kontrol etmeniz gerekiyorsa — farklı adlarla iki ayrı test oluşturun. “Test başına bir assert” olarak bilinen bu kural, Kent Beck'in (2002) önerilerine dayanır.

  • Unit döndüren yöntemler için MockK'da relaxUnitFun = true kullanın — aksi halde Mock, belirtilmemiş bir çağrıda istisna fırlatır
  • verify'i yalnızca kritik çağrılarla sınırlayın — her getter ve setter'ı doğrulamayın, bu testleri kırılgan yapar
  • ArgumentMatchers'ı anlamlı şekilde uygulayın — argüman iş mantığı için kritikse any() önemli ayrıntıları gizler
  • verifyNoMoreInteractions'ı aşırı kullanmayın — bu yöntem, testleri üretim kodundaki herhangi bir değişikliğe karşı gereksiz yere katı hale getirir
  • @MockkAnnotations kullanın Mock nesnelerinin otomatik başlatılması için — bu, tekrarı azaltır ve okunabilirliği artırır

Gelişmiş Mock test teknikleri

Temel mocking'in ötesinde, mobil geliştirmede belirli görevleri çözen gelişmiş teknikler vardır: çoklu iş parçacığı testi, Flow durumu doğrulama ve gerçek nesnelerin kısmi mocking'i.

spyK ile kısmi Mock

Spy (veya kısmi mock), çağrıları gerçek uygulamaya devreden ancak tek tek yöntemleri geçersiz kılmaya izin veren bir nesne oluşturmayı sağlar. MockK'da, spyk gerçek bir sınıf örneğine dayanarak oluşturulur: val repo = spyk(InMemoryUserRepository()). every ile tanımlanan beklentilere sahip çağrılar Mock üzerinden gider; geri kalanı gerçek nesne üzerinden gider. Spy, bağımlılık enjeksiyonunun henüz uygulanmadığı ve yalnızca bir yöntemi geçersiz kılmanız gereken eski kodları test etmek için özellikle yararlıdır.

Turbine ile StateFlow testi

Jetpack Compose kullanan modern Android projelerinde ViewModel, durumu StateFlow aracılığıyla gösterir. MockK, Flow bağımlılıklarını mock'lamaya izin verir ve Turbine kütüphanesi, emisyon doğrulamasını basitleştirir. Klasik model: Flow döndüren bir UseCase için MockK, ViewModel emisyonlarını doğrulamak için Turbine. Bu yığın, Kotlin Coroutines kullanan projeler için Android Testing belgeleri (Google, 2024) tarafından önerilir.

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()
        }
    }
}

Sıkça sorulan sorular

Mock, Mockito'dan nasıl farklıdır?

Mock bir kavramdır, davranışı doğrulayan bir Test Double türüdür. Mockito, Java ve Android'de Mock nesneleri oluşturmak için bir kütüphanedir. Diğer kütüphaneler: MockK (Kotlin), EasyMock (Java), Cuckoo (iOS).

Mock, Kotlin coroutine'leriyle nasıl çalışır?

Mock ile askıya alma işlevlerini test etmek için MockK (coEvery / coVerify) veya mockito-kotlin ile Mockito kullanın. MockK, coroutine'leri yerel olarak destekler: coEvery, bir askıya alma işlevinin davranışını tanımlar, coVerify, bir coroutine içinde çağrılmasını doğrular. Tüm askıya alma çağrıları runTest (kotlinx-coroutines-test) içinde yürütülmelidir.

Mock, tekrarlanan çağrılarda farklı değerler döndürebilir mi?

Evet. MockK'da returnsMany kullanın: every { api.getData() } returnsMany listOf(response1, response2). Mockito'da — bir thenReturn(value1).thenReturn(value2) zinciri. Bu, farklı yanıtlar döndüren sıralı çağrılarla davranışı test etmek için kullanışlıdır.

Testler arasında Mock durumu nasıl temizlenir?

MockK'da relaxed = true ile @MockK ek açıklamasını kullanın ve @After yönteminde clearMocks(mock) çağrısı yapın. Mockito'da — Mockito.reset(mock). En iyi uygulama: testler arası müdahaleyi ortadan kaldırmak için @Before ile her test için yeni bir Mock oluşturun.

Mock, Kotlin'de sealed class'ları nasıl işler?

MockK, sealed class'larla doğru şekilde çalışır: every { useCase() } returns Result.Success(data). Mockito, sealed class'ları doğrudan desteklemez ve geçici çözümler gerektirir. Bu, Kotlin projelerinde Mockito yerine MockK'nın önerilmesinin nedenlerinden biridir.

Özet

  • Mock — bağımlılıkların durumunu (assert) değil, davranışını (verify) doğrulayan bir Test Double türü
  • Mockito — Java/Android için standart, MockK — coroutine ve sealed class desteği ile Kotlin-ilk seçim
  • Temel kural: harici sınırlar (ağ, DB, sistem hizmetleri) için Mock, dahili sınıflar için gerçek nesneler
  • Over-mocking — ana anti-pattern: aşırı bağımlılık değiştirme testleri kırılgan ve yararsız hale getirir
  • Bir test — bir mantıksal kontrol: Mock için verify veya Stub için assert, aynı testte ikisi birden değil
  • ArgumentCaptor / slot — kör any() yerine Mock çağrı argümanlarını doğrulamanın doğru yolu
  • Kotlin projeleri için MockK önerilir: coEvery ve coVerify ek adaptörler olmadan coroutine'lerle yerel olarak çalışır

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun