Mock ایک متبادل شے ہے جو حقیقی جزو کے رویے کی نقل کرتی ہے اور اس کے ساتھ تعاملات کی تصدیق کرنے کی اجازت دیتی ہے۔ Stub کے برعکس، جو صرف ایک پہلے سے طے شدہ قیمت لوٹاتا ہے، Mock طریقہ کار کی پکار، منتقل کردہ دلائل اور پکاروں کی تعداد کو ریکارڈ کرتا ہے۔ Mockito (2024) کے مطابق، Mock Java اور Kotlin پروجیکٹس میں Test Double کی سب سے مقبول قسم ہے، جو موبائل ایپلیکیشنز کے 70% سے زیادہ یونٹ ٹیسٹ میں استعمال ہوتی ہے۔
اہم نکات
Mock ایک شے ہے جو mocking فریم ورک (Mockito, MockK, EasyMock) کے ذریعے بنائی جاتی ہے، جو ایک انٹرفیس یا کلاس کی نقل کرتی ہے اور اپنے طریقہ کار کی تمام پکاروں کو ریکارڈ کرتی ہے۔ ڈیولپر توقعات متعین کرتا ہے: طریقہ کار X کو دلائل Y کے ساتھ بلایا جائے گا اور Z لوٹائے گا۔ ٹیسٹ کے عمل میں آنے کے بعد، Mock تصدیق کرتا ہے کہ توقعات حقیقی پکاروں سے مماثل ہیں۔
یہ اصطلاح Test Doubles کے تھیٹر کے استعارے سے آئی ہے: Mock ایک «نقّال» ہے جو (Dummy کی طرح) صرف اسٹیج پر کھڑا نہیں ہوتا بلکہ ایک کردار ادا کرتا ہے اور تصدیق کرتا ہے کہ اس کے ساتھ تعامل درست تھا یا نہیں۔ اگر ٹیسٹ کے تحت کوڈ نے اس طریقہ کار کو نہیں بلایا جس کی Mock توقع کر رہا تھا، یا غلط دلائل کے ساتھ بلایا — تو ٹیسٹ توقع کی خلاف ورزی کے پیغام کے ساتھ ناکام ہو جاتا ہے۔
Mock فریم ورک فیکٹری کے ذریعے بنایا جاتا ہے: mockk<MyInterface>() یا Mockito.mock(MyClass.java)۔ فریم ورک ایک پراکسی شے تیار کرتا ہے جو تمام طریقہ کار پکاروں کو روکتا ہے۔ ہر پکار کا موازنہ پہلے سے متعین توقعات سے کیا جاتا ہے۔ اگر پکار توقع سے مماثل ہو — مخصوص قیمت لوٹائی جاتی ہے۔ اگر نہیں — Mock ترتیب کے مطابق ڈیفالٹ قیمت لوٹاتا ہے یا استثنا پھینکتا ہے۔
Mock ضروری ہے جب ٹیسٹ کے تحت کوڈ ایسے اجزاء کے ساتھ تعامل کرتا ہے جن کے ضمنی اثرات ہوں: سرور پر ڈیٹا بھیجنا، ڈیٹابیس میں لکھنا، لاگنگ، analytics، نیویگیشن، سسٹم ڈائیلاگ دکھانا۔ Mock کے بغیر، حقیقی بنیادی ڈھانچہ چلائے بغیر ان تعاملات کی تصدیق نہیں کی جا سکتی۔ Google Testing Blog کے مطابق، ٹیسٹ سرور شروع کیے بغیر یہ تصدیق کرنے کا واحد طریقہ ہے کہ ایپلیکیشن نے واقعی analytics واقعہ بھیجا ہے۔
Mock اور Stub کے درمیان فرق ٹیسٹنگ میں سب سے زیادہ بحث کردہ موضوعات میں سے ایک ہے۔ دونوں اقسام حقیقی انحصار کو بدلتی ہیں، لیکن بنیادی طور پر مختلف طریقوں سے۔
| معیار | Mock | Stub |
|---|---|---|
| بنیادی سوال | کیا طریقہ کار بلایا گیا؟ | کون سا نتیجہ لوٹایا گیا؟ |
| تصدیق | رویہ (verify) | حالت (assert) |
| ڈیٹا کی واپسی | اختیاری | لازمی |
| مثال | verify(analytics).logEvent(“click”) | assertEquals(5, repository.getCount()) |
| کب استعمال کریں | ضمنی اثرات | ڈیٹا کی واپسی |
فیصلے کے لیے ایک سادہ ٹیسٹ: اپنے آپ سے پوچھیں «اگر میں کوڈ کی یہ سطر حذف کروں، کیا ٹیسٹ ناکام ہوگا؟» اگر ٹیسٹ واپسی کی قیمت چیک کرتا ہے — آپ کو Stub (assert پر مبنی تصدیق) چاہیے۔ اگر ٹیسٹ چیک کرتا ہے کہ کوڈ نے صحیح دلائل کے ساتھ طریقہ کار بلایا — آپ کو Mock (verify پر مبنی تصدیق) چاہیے۔ یہ دوئی Command-Query Separation پیٹرن کی پیروی کرتی ہے: جو طریقہ کار حالت بدلتے ہیں (commands) انہیں Mock چاہیے؛ جو طریقہ کار ڈیٹا لوٹاتے ہیں (queries) انہیں Stub چاہیے۔
Mockito اور MockK کے درمیان انتخاب Kotlin میں Android پروجیکٹ کے ٹیسٹ اسٹیک کو ترتیب دیتے وقت پہلے فیصلوں میں سے ایک ہے۔ دونوں لائبریریاں ایک ہی مقصد پورا کرتی ہیں، لیکن Kotlin کی مخصوص خصوصیات کے لیے مختلف نقطہ نظر کے ساتھ۔
Mockito Java پروجیکٹس کے لیے حقیقی معیار ہے۔ ورژن 5.x بلٹ ان MockMaker کی بدولت فائنل کلاسز، سٹیٹک طریقہ کار اور کنسٹرکٹرز کے لیے mocking کی حمایت کرتا ہے۔ Kotlin پروجیکٹس کے لیے، Mockito کو اضافی سیٹ اپ کی ضرورت ہے: بہتر نحو کے لیے mockito-kotlin ایکسٹینشنز، فائنل کلاسز کے لیے mockito-inline۔ Mockito اضافی اڈاپٹر کے بغیر Kotlin coroutine اور suspend فنکشنز کی حمایت نہیں کرتا۔
MockK خاص طور پر Kotlin کے لیے بنایا گیا تھا۔ یہ مقامی طور پر coroutine (coEvery, coVerify)، sealed class، data class، آبجیکٹ سنگلٹن اور ایکسٹینشن فنکشنز کی حمایت کرتا ہے۔ MockK کا نحو لیمبڈا بلاکس کے ساتھ DSL استعمال کرتا ہے، جو Kotlin کوڈ میں قدرتی لگتا ہے۔ MockK اضافی سیٹ اپ کے بغیر پراپرٹی mocking کی بھی حمایت کرتا ہے — یہ Android پروجیکٹس کے لیے اہم ہے جو LiveData، StateFlow اور Delegates استعمال کرتے ہیں۔
// Mockito + mockito-kotlin
val repository = mock<UserRepository>()
whenever(repository.getUser(1)).thenReturn(user)
// MockK
val repository = mockk<UserRepository>()
every { repository.getUser(1) } returns user
بینچ مارکس (JVM Benchmark, 2024) ظاہر کرتے ہیں کہ MockK Java Reflections کے بجائے براہ راست Kotlin بائٹ کوڈ کے ساتھ کام کرنے کی وجہ سے Kotlin پروجیکٹس کے لیے Mockito سے 15–20% تیزی سے mock اشیاء بناتا ہے۔ ہزاروں یونٹ ٹیسٹ والے پروجیکٹس کے لیے، بلڈ سپیڈ میں فرق نمایاں ہو سکتا ہے: MockK بڑے پروجیکٹس میں مکمل ٹیسٹ رن پر 30–60 سیکنڈ بچاتا ہے۔
تین منظرناموں کا جائزہ لیں: Mock انحصار کے ساتھ ViewModel کی جانچ، API کال کی تصدیق کے ساتھ UseCase کی جانچ، اور coVerify کے ساتھ coroutine کی جانچ۔
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") }
}
}
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)
}
}
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 کا مؤثر استعمال نظم و ضبط کا تقاضا کرتا ہے۔ ان قواعد کی خلاف ورزی ٹیسٹوں کو نازک رکاوٹوں میں بدل دیتی ہے جو ہر ری فیکٹرنگ کے ساتھ ٹوٹ جاتے ہیں۔
ایک سخت قاعدہ: Mock صرف ان انحصاروں کے لیے بنائے جانے چاہئیں جو ایپلیکیشن کی حد کو عبور کرتے ہیں: API کلائنٹس، ڈیٹابیسز، فائل سسٹم، سسٹم سروسز (LocationManager, BluetoothAdapter, Camera)۔ اندرونی ایپلیکیشن کلاسز — ڈومین اینٹیٹیز، Value Objects، سادہ یوٹیلیٹیز — کو Mock سے تبدیل نہیں کیا جانا چاہیے۔ ان کے رویے کی جانچ حقیقی اشیاء کے ذریعے کی جاتی ہے۔
ہر ٹیسٹ میں بالکل ایک منطقی جانچ ہونی چاہیے — یا تو verify (Mock کے لیے) یا assert (Stub کے لیے)۔ ایک ہی ٹیسٹ میں حالت اور رویے کی تصدیق کو نہ ملائیں۔ اگر آپ کو API کال اور اس کے نتیجہ دونوں کی جانچ کرنی ہے — مختلف ناموں کے ساتھ دو الگ ٹیسٹ بنائیں۔ یہ قاعدہ، جو “فی ٹیسٹ ایک assert” کے نام سے جانا جاتا ہے، Kent Beck (2002) کی سفارشات سے آتا ہے۔
بنیادی mocking کے علاوہ، جدید تکنیکیں ہیں جو موبائل ڈیولپمنٹ میں مخصوص کاموں کو حل کرتی ہیں: ملٹی تھریڈنگ کی جانچ، Flow حالت کی تصدیق اور حقیقی اشیاء کا جزوی mocking۔
Spy (یا جزوی mock) ایک شے بنانے کی اجازت دیتا ہے جو کالوں کو حقیقی نفاذ کو سونپتی ہے لیکن انفرادی طریقہ کار کو اوور رائڈ کرنے دیتی ہے۔ MockK میں، spyk ایک حقیقی کلاس مثال کی بنیاد پر بنایا جاتا ہے: val repo = spyk(InMemoryUserRepository())۔ every کے ذریعے متعین توقعات والی کالیں Mock سے گزرتی ہیں؛ باقی حقیقی شے سے گزرتی ہیں۔ Spy خاص طور پر پرانے کوڈ کی جانچ کے لیے مفید ہے جہاں انحصار کا انجیکشن ابھی لاگو نہیں ہوا ہے اور آپ کو صرف ایک طریقہ کار کو اوور رائڈ کرنے کی ضرورت ہے۔
Jetpack Compose استعمال کرنے والے جدید Android پروجیکٹس میں، ViewModel StateFlow کے ذریعے حالت ظاہر کرتا ہے۔ MockK Flow انحصاروں کو mock کرنے کی اجازت دیتا ہے، اور Turbine لائبریری اخراج کی تصدیق کو آسان بناتی ہے۔ کلاسک پیٹرن: Flow لوٹانے والے UseCase کے لیے MockK، ViewModel کے اخراج کی تصدیق کے لیے Turbine۔ یہ اسٹیک Kotlin Coroutines استعمال کرنے والے پروجیکٹس کے لیے Android Testing دستاویزات (Google, 2024) کے ذریعے تجویز کردہ ہے۔
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()
}
}
}
اکثر پوچھے گئے سوالات
Mock ایک تصور ہے، Test Double کی ایک قسم جو رویے کی تصدیق کرتی ہے۔ Mockito Java اور Android میں Mock اشیاء بنانے کے لیے ایک لائبریری ہے۔ دیگر لائبریریاں: MockK (Kotlin)، EasyMock (Java)، Cuckoo (iOS)۔
Mock کے ساتھ suspend فنکشنز کی جانچ کے لیے MockK (coEvery / coVerify) یا mockito-kotlin کے ساتھ Mockito استعمال کریں۔ MockK coroutine کو مقامی طور پر سپورٹ کرتا ہے: coEvery ایک suspend فنکشن کے رویے کی وضاحت کرتا ہے، coVerify coroutine کے اندر اس کی کال کی تصدیق کرتا ہے۔ تمام suspend کالیں runTest (kotlinx-coroutines-test) کے اندر عمل میں آنی چاہئیں۔
ہاں۔ MockK میں returnsMany استعمال کریں: every { api.getData() } returnsMany listOf(response1, response2)۔ Mockito میں — thenReturn(value1).thenReturn(value2) کا سلسلہ۔ یہ مختلف جوابات لوٹانے والی ترتیب وار کالوں کے ساتھ رویے کی جانچ کے لیے مفید ہے۔
MockK میں relaxed = true کے ساتھ @MockK اینوٹیشن استعمال کریں اور @After طریقہ کار میں clearMocks(mock) کال کریں۔ Mockito میں — Mockito.reset(mock)۔ بہترین عمل: ٹیسٹوں کے درمیان مداخلت ختم کرنے کے لیے @Before کے ذریعے ہر ٹیسٹ کے لیے نیا Mock بنائیں۔
MockK sealed class کے ساتھ صحیح طریقے سے کام کرتا ہے: every { useCase() } returns Result.Success(data)۔ Mockito براہ راست sealed class کو سپورٹ نہیں کرتا، جس کے لیے متبادل حل درکار ہیں۔ یہ ایک وجہ ہے کہ Kotlin پروجیکٹس کے لیے Mockito کے بجائے MockK تجویز کیا جاتا ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں