MockK, mock nesneler oluşturmak için Kotlin-first bir framework'tür ve Kotlin ekosistemi için dil özellikleri (eşyordamlar, genişletme fonksiyonları, data class ve sealed class) dikkate alınarak özel olarak tasarlanmıştır. Java'dan Kotlin'e taşınan Mockito'nun aksine, MockK baştan itibaren Kotlin sözdizimi için tasarlanmıştır ve final sınıflarla çalışmak için ek eklenti gerektirmez. MockK.io'ya göre, kütüphane birim testi yapan Kotlin projelerinin %40'ından fazlasında kullanılmaktadır.
Önemli Noktalar
MockK, mock nesneler oluşturmak için Kotlin'de yazılmış ve sözdizimi için optimize edilmiş bir kütüphanedir. Mockito ile aynı sorunları çözer — test edilen kodu bağımlılıklardan ayırmak — ancak bunu Kotlin'e özgü yapıları kullanarak yapar: lambdalar, DSL, reified generics ve suspend fonksiyonları.
Taşınan çözümlere göre MockK'nın ana avantajı yerel Kotlin desteğidir. Mockito'da, bir final sınıfını mocklamak opt-in (mockito-inline) gerektirir ve statik yöntemler mockStatic gerektirir. MockK bunu varsayılan olarak destekler, çünkü Kotlin sınıfları varsayılan olarak finaldir ve bu sınırlamayı aşmak kütüphane mimarisine yerleşiktir.
1.13.12 sürümü (2024), Kotlin 2.0, K2 derleyicisi ve çoklu platform projelerini (KMP) destekleyen kararlı bir sürümdür. MockK ayrıca Kotlin/Native ve Kotlin/JS ile de çalışır ve bu, ne Mockito'nun ne de EasyMock'un uygulanamadığı KMP projeleri için onu tek seçenek haline getirir.
MockK, Kotlin'in özellikleri dikkate alınarak tasarlanmıştır ve performanstan ödün vermeden kısa ve type-safe bir API sağlamak için dil özelliklerini — reified generics, lambdalı DSL, inline fonksiyonlar — kullanır.
MockK'nın mekanizması, ByteBuddy kütüphanesi (Mockito gibi) aracılığıyla bayt kodu enstrümantasyonuna dayanır, ancak bunu Kotlin dostu bir DSL ile sarar. when().thenReturn() zincirleri yerine, MockK dilin doğal bir uzantısı gibi görünen lambda blokları every { } ve coEvery { } kullanır. Perde arkasında, MockK lambda içindeki çağrıyı yakalar, yansıma yoluyla yöntemi ve argümanları analiz eder ve kaydedilen stubbing kurallarıyla eşleştirir.
every { mock.method() } returns value bloğu “yöntem her çağrıldığında, değeri döndür” olarak okunur. Bu bildirimsel sözdizimi Kotlin stiline daha yakındır ve when() içindeki argüman sırasıyla ilgili karışıklığı ortadan kaldırır. Kotlin'in reified generics sayesinde, mock türü sınıfı açıkça belirtmeden otomatik olarak çıkarılır.
val repository = mockk<UserRepository>()
// Stubbing: findById(1)'e her çağrı kullanıcıyı döndürür
every { repository.findById(1) } returns User("Alice")
// Çağrı ve doğrulama
val result = repository.findById(1)
assertEquals("Alice", result.name)
Her yöntemin açıkça yapılandırılması gereken Mockito'nun aksine, MockK relaxed mock'u destekler — herhangi bir yöntem için “makul” varsayılan değerler döndüren bir mock: List için boş liste, Int için 0, String için boş dize. Bu, hazırlık kodu miktarını önemli ölçüde azaltır.
// Relaxed mock — tüm yöntemler varsayılan değerleri döndürür
val api = mockk<ApiService>(relaxed = true)
// Stubbing gerektirmez — boş liste döndürür
println(api.getUsers()) // []
MockK, mock nesneler oluşturmanın birkaç yolunu sunar: katı mock için mockk<T>() (her yöntem açıkça yapılandırılmalıdır), gevşek mock için mockk<T>(relaxed = true) ve gerçek bir nesne üzerinde casus oluşturmak için spyk(obj).
| Fonksiyon | Tür | Stubbing Olmadan Davranış |
|---|---|---|
| mockk() | Katı mock | Stub'lanmamış yöntem çağrıldığında istisna fırlatır |
| mockk(relaxed = true) | Gevşek mock | Varsayılan değeri döndürür |
| spyk() | Casus | Stub yapılandırılmamışsa gerçek yöntemi çağırır |
| slot() | Argument Captor | Doğrulama için argümanı yakalar |
Katı ve gevşek mock arasındaki seçim bağlama bağlıdır. Katı bir mock, testin davranışı tanımlanmamış yöntemleri kullanmamasını sağlar — bu güvenilirliği artırır. Gevşek mock, tüm bağımlılıkların önemli olmadığı hızlı test prototipleme için uygundur. Pratikte, katı bir mock ile başlamanız ve yalnızca stubbing testin kendisinden daha fazla satır kapladığında gevşeğe geçmeniz önerilir.
Every bloğu, MockK'daki merkezi stubbing yapısıdır. Lambda içinde, belirli argümanlarla bir yöntem çağrısı tanımlanır ve ardından returns ile bir değer döndürülür, throws ile bir istisna fırlatılır veya answers ile bir yanıt hesaplanır.
MockK, test için gereken tüm senaryoları destekler: değer döndürme, istisna fırlatma, argümanlara dayalı yanıt hesaplama ve sırayla birden çok yanıt (çağrı sırası).
// Değer döndürme
every { repo.findById(1) } returns User("Alice")
// İstisna fırlatma
every { repo.findById(999) } throws NotFoundException()
// Dinamik yanıt
every { repo.save(any()) } answers {
val user = firstArg<User>()
user.copy(id = 42)
}
// Yanıt sırası
every { repo.findAll() } returnsMany listOf(
listOf(User("Alice")),
listOf(User("Bob")),
emptyList()
)
Verify, MockK'da amaç olarak Mockito.verify()'e benzer, ancak Kotlin DSL kullanır: verify { mock.method() }. Suspend fonksiyonları için coVerify { mock.suspendMethod() } kullanılır, bu eşyordamlarla doğru şekilde çalışır ve özel bir çalıştırıcı gerektirmez.
MockK, Mockito ile aynı değiştiricileri destekler: exactly(1), atLeast(2), atMost(5), wasNot(Called). Sözdizimi minimaldir — değiştirici verify { } içinde ilk argüman olarak iletilir.
// Doğrulama: yöntem tam olarak 1 kez çağrıldı
verify(exactly = 1) { repo.save(any()) }
// Çağrı sırasını doğrula
verifySequence {
repo.save(any())
repo.flush()
}
// Suspend fonksiyonları için coVerify
coVerify { api.fetchUsers() }
Argüman doğrulaması için slot() kullanılır — ArgumentCaptor'un bir benzeri. Bir slot, çağrıdan önce bildirilir, every veya verify'ye iletilir ve test yürütmesinden sonra yakalanan değeri içerir.
val userSlot = slot<User>()
verify { repo.save(capture(userSlot)) }
assertEquals("Alice", userSlot.captured.name)
MockK, JUnit 5'te başlatma yoluyla mock oluşturmak için @MockK ve @RelaxedMockK ek açıklamalarını sağlar. MockKExtension, her testten önce otomatik olarak mock'lar oluşturur ve sonra temizler — MockitoExtension'a benzer, ancak gevşek mod desteğiyle.
@InjectMockKs ek açıklaması (veya açık nesne oluşturma ile alternatif @MockK), test edilen örneğe mock'ları enjekte eder. Bu, tekrar kodunu azaltır ve test kodunu daha temiz hale getirir.
@ExtendWith(MockKExtension::class)
class UserServiceTest {
@MockK
lateinit var repository: UserRepository
@InjectMockKs
lateinit var service: UserService
@Test
fun `getUser returns user from repository`() {
every { repository.findById(1) } returns User("Alice")
assertEquals("Alice", service.getUser(1)?.name)
}
}
Seçim ekip kompozisyonuna ve proje türüne bağlıdır. Mockito daha büyük bir ekosisteme, daha fazla örneğe ve entegrasyona sahiptir, ancak MockK daha temiz Kotlin sözdizimi ve dil özellikleri için yerel destek sunar. Yeni Kotlin projeleri için MockK daha deyimsel bir çözüm olarak önerilir.
| Kriter | MockK | Mockito |
|---|---|---|
| Sözdizimi | Kotlin DSL (every, verify) | Java stili (when, thenReturn) |
| Eşyordamlar | coEvery, coVerify (yerel) | Ek kütüphane gerektirir |
| Final sınıf | Varsayılan olarak desteklenir | mockito-inline gerektirir |
| KMP | Desteklenir | Desteklenmez |
| Gevşek mock | Dahili | Eşdeğeri yok |
| Popülerlik | Kotlin topluluğunda artıyor | Java ve hibrit projelerde baskın |
Saf Kotlin projeleri için (Java sınıfları olmadan), MockK tercih edilir: daha az tekrar kodu, yerel eşyordam desteği, final sınıflarla sürpriz yok. Hibrit projeler veya Java geçmişine sahip ekipler için Mockito çalışan bir seçenek olmaya devam ediyor — her iki kütüphane de farklı modüller aracılığıyla aynı projede kullanılabilir. Mockito'dan MockK'ya geçiş yaparken, @Mock ek açıklamalarını @MockK ile değiştirmek ve when().thenReturn() bloklarını every { } formatında yeniden yazmak yeterlidir.
Sıkça Sorulan Sorular
Relaxed mock, tüm stub'lanmamış yöntemler için istisna fırlatmadan varsayılan değerler (boş liste, 0, null) döndürür. Normal (katı) mock, her yöntemin açık stubbing'ini gerektirir — aksi halde test başarısız olur. Gevşek mock hızlı testler için uygundur, katı ise güvenilir testler için.
MockK, mockkStatic() aracılığıyla genişletme fonksiyonlarının mocklanmasını destekler. Bu mümkündür çünkü Kotlin'deki genişletme fonksiyonları, alıcıyı ilk parametre olarak alan statik yöntemlerdir. Her genişletme fonksiyonu için, bildirildiği sınıfı belirtmeniz gerekir.
Evet, MockK, ortak kod için Kotlin Multiplatform'u (KMP) destekler. JVM, Native ve JS platformlarında ortak API'yi kullanabilirsiniz: mockk(), every, verify. Bu, MockK'yı Mockito'nun çalışmadığı KMP projeleri için tek seçenek haline getirir.
verifySequence { } kullanın — çağrıların beklenen sırada kesin olarak belirtildiği bir blok. Gerçek sıra farklıysa, verifySequence ilk uyuşmayan çağrıyı belirten bir istisna fırlatır.
Evet, teknik olarak mümkündür, ancak önerilmez. Bayt kodu enstrümantasyonu seviyesinde (ByteBuddy vs mockito-inline) çakışmalar oluşabilir. Bir proje zaten Mockito kullanıyorsa, MockK'ya geçiş modül izolasyonu yoluyla kademeli olabilir.
Özet
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.
Ayrıca okuyun