MockK موک آبجیکٹ بنانے کے لیے ایک Kotlin-first فریم ورک ہے، جو خاص طور پر Kotlin ایکو سسٹم کے لیے اس کی زبان کی خصوصیات کو مدنظر رکھتے ہوئے ڈیزائن کیا گیا ہے: کوریوٹینز، ایکسٹینشن فنکشنز، data class اور sealed class۔ Mockito کے برعکس، جسے Java سے Kotlin میں پورٹ کیا گیا تھا، MockK ابتدا سے ہی Kotlin نحو کے لیے ڈیزائن کیا گیا تھا اور final کلاسز کے ساتھ کام کرنے کے لیے اضافی پلگ ان کی ضرورت نہیں ہے۔ MockK.io کے مطابق، لائبریری یونٹ ٹیسٹنگ والے Kotlin پروجیکٹس کے 40% سے زیادہ میں استعمال ہوتی ہے۔
اہم نکات
MockK موک آبجیکٹ بنانے کے لیے ایک لائبریری ہے، جو Kotlin میں لکھی گئی ہے اور اس کے نحو کے لیے بہتر بنائی گئی ہے۔ یہ وہی مسائل حل کرتی ہے جو Mockito حل کرتا ہے — ٹیسٹ شدہ کوڈ کو انحصار سے الگ کرنا — لیکن یہ Kotlin کے مخصوص تعمیرات استعمال کرکے کرتی ہے: لیمبڈا، DSL، reified generics اور suspend فنکشنز۔
پورٹ کیے گئے حل پر MockK کا بنیادی فائدہ مقامی Kotlin سپورٹ ہے۔ Mockito میں، final کلاس کو موک کرنے کے لیے opt-in (mockito-inline) کی ضرورت ہوتی ہے، اور جامد طریقوں کے لیے mockStatic کی ضرورت ہوتی ہے۔ MockK ڈیفالٹ طور پر اس کی حمایت کرتا ہے، کیونکہ Kotlin کلاسیں ڈیفالٹ طور پر final ہوتی ہیں، اور اس حد کو نظرانداز کرنا لائبریری کے فن تعمیر میں شامل ہے۔
ورژن 1.13.12 (2024) ایک مستحکم ریلیز ہے جو Kotlin 2.0، K2 کمپائلر اور ملٹی پلیٹ فارم پروجیکٹس (KMP) کو سپورٹ کرتی ہے۔ MockK Kotlin/Native اور Kotlin/JS کے ساتھ بھی کام کرتا ہے، جو اسے KMP پروجیکٹس کے لیے واحد انتخاب بناتا ہے جہاں نہ تو Mockito اور نہ ہی EasyMock لاگو ہوتے ہیں۔
MockK کو Kotlin کی خصوصیات کو مدنظر رکھتے ہوئے ڈیزائن کیا گیا ہے اور کارکردگی کی قربانی کے بغیر ایک جامع اور type-safe API فراہم کرنے کے لیے زبان کی خصوصیات — reified generics، لیمبڈا کے ساتھ DSL، ان لائن فنکشنز — استعمال کرتا ہے۔
MockK کا طریقہ کار ByteBuddy لائبریری (Mockito کی طرح) کے ذریعے بائٹ کوڈ انسٹرومینٹیشن پر مبنی ہے، لیکن اسے Kotlin دوست DSL میں لپیٹتا ہے۔ when().thenReturn() زنجیروں کے بجائے، MockK لیمبڈا بلاکس every { } اور coEvery { } استعمال کرتا ہے جو زبان کی قدرتی توسیع کی طرح نظر آتے ہیں۔ ہڈ کے نیچے، MockK لیمبڈا کے اندر کال کو روکتا ہے، ریفلیکشن کے ذریعے طریقہ اور دلائل کا تجزیہ کرتا ہے اور انہیں ریکارڈ شدہ سٹیبنگ قوانین سے ملاتا ہے۔
بلاک every { mock.method() } returns value کو “جب بھی طریقہ بلایا جائے، قدر لوٹائیں” کے طور پر پڑھا جاتا ہے۔ یہ اعلانیہ نحو Kotlin انداز کے قریب ہے اور when() میں دلیل کی ترتیب کے ساتھ الجھن کو ختم کرتا ہے۔ Kotlin کے reified generics کی بدولت، موک کی قسم کلاس کو واضح طور پر بتائے بغیر خود بخود معلوم ہو جاتی ہے۔
val repository = mockk<UserRepository>()
// Stubbing: findById(1) کی ہر کال صارف کو لوٹاتی ہے
every { repository.findById(1) } returns User("Alice")
// کال اور تصدیق
val result = repository.findById(1)
assertEquals("Alice", result.name)
Mockito کے برعکس، جہاں ہر طریقہ کو واضح طور پر ترتیب دینا ضروری ہے، MockK relaxed mock کو سپورٹ کرتا ہے — ایک موک جو کسی بھی طریقے کے لیے “مناسب” ڈیفالٹ اقدار لوٹاتا ہے: List کے لیے خالی فہرست، Int کے لیے 0، String کے لیے خالی سٹرنگ۔ یہ سیٹ اپ کوڈ کی مقدار کو ڈرامائی طور پر کم کرتا ہے۔
// Relaxed mock — تمام طریقے ڈیفالٹ اقدار لوٹاتے ہیں
val api = mockk<ApiService>(relaxed = true)
// سٹیبنگ کی ضرورت نہیں — خالی فہرست لوٹاتا ہے
println(api.getUsers()) // []
MockK موک آبجیکٹ بنانے کے کئی طریقے فراہم کرتا ہے: سخت موک کے لیے mockk<T>() (ہر طریقہ کو واضح طور پر ترتیب دیا جانا چاہیے)، ریلیکسڈ موک کے لیے mockk<T>(relaxed = true) اور حقیقی آبجیکٹ پر جاسوس بنانے کے لیے spyk(obj)۔
| فنکشن | قسم | سٹیبنگ کے بغیر رویہ |
|---|---|---|
| mockk() | سخت موک | غیر سٹیب شدہ طریقہ بلانے پر استثنا پھینکتا ہے |
| mockk(relaxed = true) | ریلیکسڈ موک | ڈیفالٹ قدر لوٹاتا ہے |
| spyk() | جاسوس | اگر سٹیب ترتیب نہ دیا گیا ہو تو حقیقی طریقہ بلاتا ہے |
| slot() | Argument Captor | تصدیق کے لیے دلیل کو پکڑتا ہے |
سخت اور ریلیکسڈ موک کے درمیان انتخاب سیاق و سباق پر منحصر ہے۔ سخت موک اس بات کی ضمانت دیتا ہے کہ ٹیسٹ ان طریقوں کو استعمال نہیں کرتا جن کا رویہ متعین نہیں ہے — یہ وشوسنییتا بڑھاتا ہے۔ ریلیکسڈ موک فوری ٹیسٹ پروٹو ٹائپنگ کے لیے آسان ہے جہاں تمام انحصار اہم نہیں ہوتے۔ عملی طور پر، سخت موک سے شروع کرنے اور صرف اس وقت ریلیکسڈ پر سوئچ کرنے کی سفارش کی جاتی ہے جب سٹیبنگ ٹیسٹ سے زیادہ لائنیں لے۔
Every بلاک MockK میں مرکزی سٹیبنگ تعمیر ہے۔ لیمبڈا کے اندر، مخصوص دلائل کے ساتھ ایک طریقہ کال بیان کی جاتی ہے، اور پھر returns کے ذریعے ایک قدر لوٹائی جاتی ہے، throws کے ذریعے استثنا پھینکا جاتا ہے، یا answers کے ذریعے جواب کا حساب لگایا جاتا ہے۔
MockK ٹیسٹنگ کے لیے تمام ضروری منظرناموں کو سپورٹ کرتا ہے: قدر لوٹانا، استثنا پھینکنا، دلائل کی بنیاد پر جواب کا حساب لگانا، اور ترتیب میں متعدد جوابات (کال ترتیب)۔
// قدر لوٹانا
every { repo.findById(1) } returns User("Alice")
// استثنا پھینکنا
every { repo.findById(999) } throws NotFoundException()
// متحرک جواب
every { repo.save(any()) } answers {
val user = firstArg<User>()
user.copy(id = 42)
}
// جوابات کی ترتیب
every { repo.findAll() } returnsMany listOf(
listOf(User("Alice")),
listOf(User("Bob")),
emptyList()
)
Verify MockK میں مقصد میں Mockito.verify() سے ملتا جلتا ہے، لیکن Kotlin DSL استعمال کرتا ہے: verify { mock.method() }۔ Suspend فنکشنز کے لیے coVerify { mock.suspendMethod() } استعمال ہوتا ہے، جو کوریوٹینز کے ساتھ صحیح طریقے سے کام کرتا ہے اور کسی خاص رنر کی ضرورت نہیں ہے۔
MockK وہی موڈیفائر سپورٹ کرتا ہے جو Mockito کرتا ہے: exactly(1), atLeast(2), atMost(5), wasNot(Called)。 نحو کم سے کم ہے — موڈیفائر کو verify { } میں پہلی دلیل کے طور پر پاس کیا جاتا ہے۔
// تصدیق: طریقہ بالکل 1 بار بلایا گیا
verify(exactly = 1) { repo.save(any()) }
// کال کی ترتیب کی تصدیق
verifySequence {
repo.save(any())
repo.flush()
}
// Suspend فنکشنز کے لیے coVerify
coVerify { api.fetchUsers() }
دلائل کی تصدیق کے لیے slot() استعمال ہوتا ہے — ArgumentCaptor کا ایک مشابہ۔ Slot کال سے پہلے اعلان کیا جاتا ہے، every یا verify میں پاس کیا جاتا ہے، اور ٹیسٹ کے نفاذ کے بعد پکڑی گئی قدر پر مشتمل ہوتا ہے۔
val userSlot = slot<User>()
verify { repo.save(capture(userSlot)) }
assertEquals("Alice", userSlot.captured.name)
MockK JUnit 5 میں ابتدا کے ذریعے موک بنانے کے لیے @MockK اور @RelaxedMockK تشریحات فراہم کرتا ہے۔ MockKExtension ہر ٹیسٹ سے پہلے خود بخود موک بناتا ہے اور بعد میں صاف کرتا ہے — MockitoExtension کی طرح، لیکن ریلیکسڈ موڈ سپورٹ کے ساتھ۔
@InjectMockKs تشریح (یا واضح آبجیکٹ تخلیق کے ساتھ متبادل @MockK) ٹیسٹ شدہ مثال میں موک انجیکٹ کرتی ہے۔ یہ بوائلر پلیٹ کو کم کرتی ہے اور ٹیسٹ کوڈ کو صاف بناتی ہے۔
@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)
}
}
انتخاب کا انحصار ٹیم کی تشکیل اور پروجیکٹ کی قسم پر ہے۔ Mockito کا ماحولیاتی نظام بڑا ہے، زیادہ مثالیں اور انضمام ہیں، لیکن MockK صاف ستھرا Kotlin نحو اور زبان کی خصوصیات کے لیے مقامی سپورٹ فراہم کرتا ہے۔ نئے Kotlin پروجیکٹس کے لیے، MockK کو زیادہ محاوراتی حل کے طور پر تجویز کیا جاتا ہے۔
| معیار | MockK | Mockito |
|---|---|---|
| نحو | Kotlin DSL (every, verify) | Java طرز (when, thenReturn) |
| کوریوٹینز | coEvery, coVerify (مقامی) | اضافی لائبریریوں کی ضرورت |
| Final کلاس | ڈیفالٹ طور پر معاونت | mockito-inline کی ضرورت |
| KMP | معاونت شدہ | معاونت نہیں |
| Relaxed mock | بلٹ ان | کوئی مساوی نہیں |
| مقبولیت | Kotlin کمیونٹی میں بڑھتی ہوئی | Java اور ہائبرڈ پروجیکٹس میں غالب |
خالص Kotlin پروجیکٹس کے لیے (Java کلاسز کے بغیر)، MockK افضل ہے: کم بوائلر پلیٹ، مقامی کوریوٹین سپورٹ، final کلاسز کے ساتھ کوئی حیرانی نہیں۔ ہائبرڈ پروجیکٹس یا Java پس منظر والی ٹیموں کے لیے، Mockito ایک کام کرنے والا آپشن ہے — دونوں لائبریریاں مختلف ماڈیولز کے ذریعے ایک ہی پروجیکٹ میں استعمال کی جا سکتی ہیں۔ Mockito سے MockK میں منتقلی کرتے وقت، @Mock تشریحات کو @MockK سے بدلنا اور when().thenReturn() بلاکس کو every { } فارمیٹ میں دوبارہ لکھنا کافی ہے۔
اکثر پوچھے گئے سوالات
Relaxed mock تمام غیر سٹیب شدہ طریقوں کے لیے استثنا پھینکے بغیر ڈیفالٹ اقدار (خالی فہرست، 0، null) لوٹاتا ہے۔ عام (سخت) موک کو ہر طریقہ کے واضح سٹیبنگ کی ضرورت ہوتی ہے — ورنہ ٹیسٹ ناکام ہو جاتا ہے۔ ریلیکسڈ موک فوری ٹیسٹوں کے لیے آسان ہے، سخت قابل اعتماد ٹیسٹوں کے لیے۔
MockK mockkStatic() کے ذریعے ایکسٹینشن فنکشنز کی موکنگ کو سپورٹ کرتا ہے۔ یہ ممکن ہے کیونکہ Kotlin میں ایکسٹینشن فنکشنز جامد طریقے ہیں جن میں پہلے پیرامیٹر کے طور پر وصول کنندہ ہوتا ہے۔ ہر ایکسٹینشن فنکشن کے لیے، آپ کو وہ کلاس بتانی ہوگی جس میں اس کا اعلان کیا گیا ہے۔
ہاں، MockK مشترکہ کوڈ کے لیے Kotlin Multiplatform (KMP) کو سپورٹ کرتا ہے۔ JVM، Native اور JS پلیٹ فارمز پر، آپ مشترکہ API: mockk(), every, verify استعمال کر سکتے ہیں۔ یہ MockK کو KMP پروجیکٹس کے لیے واحد انتخاب بناتا ہے جہاں Mockito کام نہیں کرتا۔
verifySequence { } استعمال کریں — ایک بلاک جہاں کالز متوقع ترتیب میں سختی سے متعین کی جاتی ہیں۔ اگر حقیقی ترتیب مختلف ہو، verifySequence پہلی غیر مماثل کال بتاتے ہوئے استثنا پھینکے گا۔
ہاں، تکنیکی طور پر ممکن ہے، لیکن تجویز نہیں کیا جاتا۔ بائٹ کوڈ انسٹرومینٹیشن کی سطح پر (ByteBuddy بمقابلہ mockito-inline) تنازعات پیدا ہو سکتے ہیں۔ اگر پروجیکٹ پہلے سے Mockito استعمال کرتا ہے، تو MockK میں منتقلی ماڈیول علیحدگی کے ذریعے بتدریج ہو سکتی ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں