MockK — bu Kotlin-first freymvorkdur mock-obyektləri yaratmaq üçün, Kotlin ekosistemi üçün xüsusilə onun dil xüsusiyyətləri nəzərə alınmaqla hazırlanmışdır: korutinlər, extension-funksiyalar, data class və sealed class. Java-dan Kotlin-ə port edilmiş Mockito-dan fərqli olaraq, MockK əvvəldən Kotlin sintaksisi üçün layihələndirilmişdir və final-klaslarla işləmək üçün əlavə plaginlər tələb etmir. MockK.io məlumatlarına görə, kitabxana vahid testlərlə Kotlin layihələrinin 40%-dən çoxunda istifadə olunur.
Əsas məqamlar
MockK — bu Kotlin dilində yazılmış və onun sintaksisi üçün optimallaşdırılmış mock-obyektləri yaratma kitabxanasıdır. O, Mockito ilə eyni vəzifələri həll edir — test edilən kodu asılılıqlardan təcrid etmək — lakin bunu Kotlin-ə xas konstruksiyalardan istifadə edərək edir: lambdalar, DSL, reified generics və suspend-funksiyalar.
MockK-nın port edilmiş həllər üzərində əsas üstünlüyü Kotlin-in nativ dəstəyidir. Mockito-da final-klasların mock edilməsi opt-in tələb edir (mockito-inline), statik metodlar isə — mockStatic. MockK bunu standart olaraq dəstəkləyir, çünki Kotlin klasları standart olaraq finaldır və bu məhdudiyyətin aradan qaldırılması kitabxananın arxitekturasına daxil edilmişdir.
1.13.12 versiyası (2024) — Kotlin 2.0, K2-kompilyator və çoxplatformalı layihələri (KMP) dəstəkləyən sabit buraxılışdır. MockK həmçinin Kotlin/Native və Kotlin/JS ilə işləyir ki, bu da onu nə Mockito, nə də EasyMock-un tətbiq oluna bilmədiyi KMP layihələri üçün yeganə seçim edir.
MockK Kotlin-in spesifikasını nəzərə alaraq layihələndirilmişdir və dil imkanlarından — reified generics, lambdalarla DSL, inline-funksiyalar — məhsuldarlığı itirmədən yığcam və tip təhlükəsiz API təmin etmək üçün istifadə edir.
Mexanizm MockK ByteBuddy kitabxanası vasitəsilə baytkod-instrumentasiyasəna əsaslanır (Mockito kimi), lakin onu Kotlin-ə uyğun DSL-ə bürüyür. when().thenReturn() zəncirləri əvəzinə MockK every { } və coEvery { } lambda bloklarından istifadə edir ki, bunlar da dilin təbii genişlənməsi kimi görünür. Daxildə, MockK lambda daxilində çağırışı tutur, metodu və arqumentləri refleksiya vasitəsilə təhlil edir və qeyd edilmiş stubbing qaydaları ilə uyğumlaşdırır.
every { mock.method() } returns value bloku „hər dəfə metod çağırılanda dəyər qaytar” kimi oxunur. Bu bəyannamə sintaksisi Kotlin üslubuna daha yaxındır və when()-də arqumentlərin sırası ilə bağlı qarışıqlığı aradan qaldırır. Kotlin-in reified generics sayəsində, mock tipi sinfin açıq göstərilməsi olmadan avtomatik olaraq çıxarılır.
val repository = mockk<UserRepository>()
// Stubbing: hər findById(1) çağırışı istifadəçini qaytarır
every { repository.findById(1) } returns User("Alice")
// Çağırış və yoxlama
val result = repository.findById(1)
assertEquals("Alice", result.name)
Hər metodun açıq şəkildə konfiqurasiya edilməli olduğu Mockito-dan fərqli olaraq, MockK relaxed mock — istənilən metod üçün „məntıqlı” standart dəyərlər qaytaran mock-u dəstəkləyir: List üçün boş siyahı, Int üçün 0, String üçün boş sətr. Bu, hazırlıq kodunun həcmini kəskin şəkildə azaldır.
// Relaxed mock — bütün metodlar standart dəyərlər qaytarır
val api = mockk<ApiService>(relaxed = true)
// Stubbing tələb etmir — boş siyahı qaytaracaq
println(api.getUsers()) // []
MockK mock-obyektləri yaratmağın bir neçe üsulunu təklif edir: ciddi mock üçün mockk<T>() (hər metod açıq şəkildə konfiqurasiya edilməlidir), relaks edilmiş mock üçün mockk<T>(relaxed = true) və real obyekt üzərində casus yaratmaq üçün spyk(obj).
| Funksiya | Tip | Stubbing olmadan davranış |
|---|---|---|
| mockk() | Ciddi mock | Stub edilməmiş metod çağırılanda istisna atır |
| mockk(relaxed = true) | Relaks edilmiş mock | Standart dəyər qaytarır |
| spyk() | Casus | Stub konfiqurasiya edilməyibsə, real metodu çağırır |
| slot() | Argument Captor | Yoxlama üçün arqumenti tutur |
Ciddi və relaks edilmiş mock arasında seçim kontekstdən asılıdır. Ciddi mock, testin davranışı müəyyən edilməmiş metodlardan istifadə etməməsini təmin edir — bu etibarlılığı artırır. Relaks edilmiş mock, bütün asılılıqların vacib olmadığı sürətli test prototipləşdirmə üçün əlverişlidir. Təcrübədə, ciddi mock-dan başlamaq və yalnız stubbing testin özündən daha çox sətir tutduqda relaxed-a keçmək tövsiyə olunur.
Every bloku — MockK-da stubbing-in mərkəzi konstruksiyasıdır. Lambda daxilində konkret arqumentlərlə metod çağırışı təsvir edilir və sonra returns vasitəsilə dəyər qaytarılır, throws vasitəsilə istisna atılır və ya answers vasitəsilə cavab hesablanır.
MockK test üçün lazım olan bütün ssenariləri dəstəkləyir: dəyər qaytarma, istisna atma, arqumentlər əsasında cavab hesablama, ardıcıl çağırışlar üçün bir neçə cavab.
// Dəyər qaytarma
every { repo.findById(1) } returns User("Alice")
// İstisna atma
every { repo.findById(999) } throws NotFoundException()
// Dinamik cavab
every { repo.save(any()) } answers {
val user = firstArg<User>()
user.copy(id = 42)
}
// Cavablar ardıcıllığı
every { repo.findAll() } returnsMany listOf(
listOf(User("Alice")),
listOf(User("Bob")),
emptyList()
)
Verify MockK-da mənaca Mockito.verify()-a bənzərdir, lakin Kotlin DSL-dən istifadə edir: verify { mock.method() }. Suspend-funksiyalar üçün coVerify { mock.suspendMethod() } istifadə olunur ki, bu da korutinlərlə düzgün işləyir və xüsusi runner tələb etmir.
MockK Mockito ilə eyni modifikatorları dəstəkləyir: exactly(1), atLeast(2), atMost(5), wasNot(Called). Sintaksis minimalistdir — modifikator verify { }-də ilk arqument kimi ötürülür.
// Yoxlama: metod dəqiq 1 dəfə çağırıldı
verify(exactly = 1) { repo.save(any()) }
// Çağırış sırasını yoxlama
verifySequence {
repo.save(any())
repo.flush()
}
// Suspend-funksiyalar üçün coVerify
coVerify { api.fetchUsers() }
Arqumentləri yoxlamaq üçün slot() — ArgumentCaptor-un analoqu istifadə olunur. Slot çağırışdan əvvəl elan edilir, every və ya verify-ə ötürülür və test yerinə yetirildikdən sonra tutulmuş dəyəri ehtiva edir.
val userSlot = slot<User>()
verify { repo.save(capture(userSlot)) }
assertEquals("Alice", userSlot.captured.name)
MockK JUnit 5-də inisializasiya vasitəsilə mocklar yaratmaq üçün @MockK və @RelaxedMockK annotasiyalarını təqdim edir. MockKExtension genişləndirməsi hər testdən əvvəl avtomatik olaraq mocklar yaradır və sonra təmizləyir — MockitoExtension-a bənzərdir, lakin relaxed rejimi dəstəkləyir.
@InjectMockKs annotasiyası (və ya alternativ olaraq @MockK obyektin açıq yaradılması ilə) mockları test edilən nüsxəyə daxil edir. Bu, boilerplate-i azaldır və test kodunu təmizləyir.
@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 MockK və Mockito arasında komandanın tərkibindən və layihə tipindən asılıdır. Mockito daha böyük ekosistemə, daha çox nümunə və inteqrasiyaya malikdir, lakin MockK daha təmiz Kotlin sintaksisi və dil xüsusiyyətlərinin nativ dəstəyini təmin edir. Yeni Kotlin layihələri üçün MockK daha idiomatik həll kimi tövsiyə olunur.
| Meyar | MockK | Mockito |
|---|---|---|
| Sintaksis | Kotlin DSL (every, verify) | Java stil (when, thenReturn) |
| Korutinlər | coEvery, coVerify (nativ) | Əlavə kitabxanalar tələb edir |
| Final class | Standart olaraq dəstəklənir | mockito-inline tələb edir |
| KMP | Dəstəklənir | Dəstəklənmir |
| Relaxed mock | Daxili | Analoqu yoxdur |
| Populyarlıq | Kotlin icmasında artır | Java və hibrid layihələrdə üstünlük təşkil edir |
Təmiz Kotlin layihələri üçün (Java klasları olmadan) MockK üstünlük təşkil edir: daha az boilerplate, korutinlərin nativ dəstəyi, final-klaslarla bağlı sürprizlər yoxdur. Hibrid layihələr və ya Java təcrübəsi olan komandalar üçün Mockito işlək variant olaraq qalır — hər iki kitabxana eyni layihədə müxtəlif modullar vasitəsilə istifadə edilə bilər. Mockito-dan MockK-a miqrasiya edərkən @Mock annotasiyalarını @MockK ilə əvəz etmək və when().thenReturn() bloklarını every { } formatına çevirmək kifayətdir.
Tez-tez verilən suallar
Relaxed mock bütün stub edilməmiş metodlar üçün standart dəyərlər qaytarır (boş siyahı, 0, null), istisna atmadan. Adi (strict) mock hər metodun açıq stubbingini tələb edir — əks halda test uğursuz olur. Relaxed mock sürətli testlər üçün əlverişlidir, strict isə etibarlılıq üçün.
MockK extension-funksiyaların mockkStatic() vasitəsilə mock edilməsini dəstəkləyir. Bu mümkündür, çünki Kotlin-də extension-funksiyalar ilk parametri qəbul edən statik metodlardır. Hər extension-funksiya üçün onun elan edildiyi sinif göstərilməlidir.
Bəli, MockK ümumi kod üçün Kotlin Multiplatform (KMP) dəstəkləyir. JVM, Native və JS platformalarında ümumi API-dən istifadə etmək olar: mockk(), every, verify. Bu, MockK-nı Mockito-nun işləmədiyi KMP layihƏəri üçün yeganə seçim edir.
verifySequence { } istifadə edin — çağırışların gözlənilən ardıcıllıqla göstərildiyi blok. Əgər real ardıcıllıq fərqlidirsə, verifySequence ilk uyğunsuz çağırışı göstərərək istisna atır.
Bəli, texniki olaraq mümkündür, lakin tövsiyə edilmir. Münaqişələr baytkod-instrumentasiya səviyyəsində yarana bilər (ByteBuddy vs mockito-inline). Layihə artıq Mockito istifadə edirsə, MockK-a miqrasiya modulların izolyasiyası vasitəsilə tədricən həyata keçirilə bilər.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun