Test Doubles وہ متبادل اشیاء ہیں جو اصلی انحصار کی بجائے یونٹ ٹیسٹنگ میں استعمال ہوتی ہیں۔ یہ اصطلاح Gerard Meszaros نے کتاب «xUnit Test Patterns» (2007) میں Mock، Stub، Fake، Spy اور Dummy کے لیے ایک عام تصور کے طور پر متعارف کرائی تھی۔ Martin Fowler (2024) کے مطابق، Test Doubles ٹیسٹ کیے جانے والے جزو کو اس کے ماحول سے الگ کرنے کی اجازت دیتے ہیں، جس سے ٹیسٹ قطعی، تیز اور بیرونی خدمات سے آزاد ہو جاتے ہیں۔
اہم نکات
Test Doubles آٹوموٹیو انڈسٹری (اسٹنٹ ڈبل) سے سافٹ ویئر ڈویلپمنٹ میں لایا گیا ایک اصطلاح ہے۔ جس طرح ایک اسٹنٹ ڈبل خطرناک منظر میں اداکار کی جگہ لیتا ہے، اسی طرح Test Double ٹیسٹ منظر نامے میں اصلی جزو کی جگہ لیتا ہے۔ یہ ضروری ہے جب اصلی انحصار دستیاب نہ ہو، سست ہو، غیر قطعی ہو، یا اس کے مضر اثرات ہوں۔
Test Double کا تصور پانچ مخصوص اقسام پر محیط ہے، ہر ایک اپنا کام حل کرتا ہے۔ Meszaros کی درجہ بندی معیاری ہے اور تمام جدید ٹیسٹنگ گائیڈز میں استعمال ہوتی ہے۔ اقسام کے درمیان فرق کنٹرول اور تصدیق کی ڈگری میں ہے: سادہ پیرامیٹر بھرنے (Dummy) سے لے کر مکمل کال ترتیب کی تصدیق (Mock) تک۔
Test Doubles کا بنیادی مقصد ٹیسٹ کے تحت ماڈیول کو الگ کرنا ہے۔ موبائل ڈویلپمنٹ میں، اصلی انحصار میں API سرورز، ڈیٹا بیسز، فائل سسٹم، ڈیوائس سینسرز اور سسٹم سروسز (LocationManager، Camera، Bluetooth) شامل ہیں۔ ان اجزاء کا براہ راست استعمال ٹیسٹ کو سست، نازک اور ماحول پر منحصر بنا دیتا ہے۔ Google Testing Blog (2023) کے مطابق، اچھی طرح سے الگ تھلگ یونٹ ٹیسٹ ملی سیکنڈز میں چلتے ہیں، جبکہ انٹیگریشن ٹیسٹ سیکنڈز اور منٹوں میں چلتے ہیں۔
Gerard Meszaros کی درجہ بندی میں پانچ اقسام کے Test Doubles شامل ہیں، جو رویے اور مقصد میں مختلف ہیں۔ ان کے درمیان فرق کو سمجھنا ماہر یونٹ ٹیسٹنگ کی بنیاد ہے۔
Dummy ایک شے ہے جو ٹیسٹ کیے جانے والے طریقے میں منتقل کی جاتی ہے لیکن کبھی استعمال نہیں ہوتی۔ Dummy صرف طریقہ کے دستخط کو پورا کرنے کے لیے ضروری ہے۔ Kotlin میں، یہ اکثر null، emptyList()، یا اسٹبس والی شے ہوتی ہے۔ Dummy میں کوئی منطق نہیں ہونی چاہیے — اگر اسے بلایا جائے تو ٹیسٹ ناکام ہو جانا چاہیے۔
Fake ایک انٹرفیس کا آسان لیکن کام کرنے والا نفاذ ہے۔ Mock اور Stub کے برعکس، Fake میں اصلی کاروباری منطق ہوتی ہے، لیکن آسان شکل میں۔ ایک کلاسک مثال InMemoryUserRepository ہے، جو ڈیٹا بیس کی بجائے HashMap میں ڈیٹا ذخیرہ کرتا ہے۔ Fake اس وقت استعمال ہوتا ہے جب آپ کو حالت پر منحصر منطق کی جانچ کرنی ہو، لیکن اصلی انفراسٹرکچر کے اوورہیڈ کے بغیر۔
| قسم | مقصد | مثال |
|---|---|---|
| Dummy | پیرامیٹر بھرنا | null، خالی شے |
| Fake | کام کرنے والا آسان نفاذ | InMemoryRepository |
| Stub | مقررہ قدر لوٹانا | when(api.getUser()).thenReturn(user) |
| Spy | تصدیق کے لیے کالز ریکارڈ کرنا | verify(spy).save(user) |
| Mock | تعامل کی تصدیق | verify(mock).sendEmail(email) |
Stub مخصوص کالز پر پہلے سے طے شدہ اقدار لوٹاتا ہے۔ Stub یہ نہیں چیک کرتا کہ اسے بلایا گیا یا نہیں — یہ صرف ڈیٹا فراہم کرتا ہے۔ Mockito میں، Stub when(method).thenReturn(value) کے ذریعے بنایا جاتا ہے۔ Stub ٹیسٹ کے لیے مثالی ہے جب آپ کو کسی انحصار سے ایک مخصوص قدر لوٹانے کی ضرورت ہو، لیکن کال کی حقیقت خود اہم نہ ہو۔
Spy ایک اصلی شے کے گرد ایک لفافہ ہے جو بعد میں تصدیق کے لیے تمام کالز ریکارڈ کرتا ہے۔ Mock کے برعکس، Spy کالز کو اصلی شے کو سونپتا ہے لیکن یہ چیک کرنے کی اجازت دیتا ہے کہ وہ ہوئیں۔ Mockito میں، Spy spy(realObject) کے ذریعے بنایا جاتا ہے۔ Spy جزوی mocking کے لیے مفید ہے، جب آپ اصلی شے استعمال کرنا چاہتے ہیں لیکن کچھ کالز کی تصدیق کرنا چاہتے ہیں۔
Mock پہلے سے طے شدہ کال توقعات والی ایک شے ہے۔ Mock تصدیق کرتا ہے کہ مخصوص طریقوں کو مخصوص دلائل اور مخصوص ترتیب میں بلایا گیا۔ Stub کے برعکس، Mock ڈیٹا کی واپسی کے بجائے رویے کی تصدیق پر توجہ مرکوز کرتا ہے۔ Mock موبائل ڈویلپمنٹ میں سب سے طاقتور اور سب سے زیادہ استعمال ہونے والی Test Double قسم ہے۔
Mock اور Stub کے درمیان فرق تجربہ کار ڈویلپرز کے درمیان بھی اکثر الجھن کا سبب بنتا ہے۔ بنیادی فرق مقصد میں ہے: Stub حالت کی تصدیق (state verification) کرتا ہے، Mock رویے کی تصدیق (behavior verification) کرتا ہے۔
Stub سوال کا جواب دیتا ہے: «کیا کوڈ نے صحیح نتیجہ لوٹایا؟»۔ Mock سوال کا جواب دیتا ہے: «کیا کوڈ نے صحیح دلائل کے ساتھ صحیح طریقوں کو بلایا؟»۔ موبائل ڈویلپمنٹ میں، Stub اس وقت استعمال ہوتا ہے جب نتیجہ اہم ہو (مثال کے طور پر، ریپوزٹری سے ڈیٹا)، جبکہ Mock اس وقت استعمال ہوتا ہے جب مضر اثرات اہم ہوں (مثال کے طور پر، ای میل بھیجنا، ڈیٹا بیس میں لکھنا)۔
// Stub: حالت کی تصدیق
every { repository.getUsers() } returns listOf(user)
val result = useCase.getUsers()
assertEquals(1, result.size)
// Mock: رویے کی تصدیق
every { analytics.logEvent("purchase") } returns Unit
useCase.purchase(item)
verify { analytics.logEvent("purchase") }
MockK استعمال کرتے ہوئے Kotlin میں پانچوں اقسام کے Test Doubles کی عملی مثالیں — Android منصوبوں کے لیے سب سے مشہور mocking لائبریری۔
class InMemoryUserRepository : UserRepository {
private val store = mutableMapOf<String, User>()
override fun save(user: User) {
store[user.email] = user
}
override fun findByEmail(email: String): User? {
return store[email]
}
}
class RegisterUseCaseTest {
private val api = mockk<AuthApi>()
private val repo = spyk(InMemoryUserRepository())
private val useCase = RegisterUseCase(api, repo)
fun `register user successfully`() = runTest {
// Stub: مقررہ API جواب لوٹانا
coEvery { api.register("test@test.com") } returns AuthResult.Success("token123")
val result = useCase.execute("test@test.com")
// Verify: تصدیق کریں کہ صارف محفوظ ہو گیا
verify { repo.save(any()) }
assertTrue(result.isSuccess())
}
}
data class Logger(val appContext: Context, val format: FormatType)
fun `test logger with dummy context`() {
// Dummy: Logger کے اندر Context استعمال نہیں ہوتا
val dummyContext = mockk<Context>()
val logger = Logger(dummyContext, FormatType.JSON)
assertEquals(FormatType.JSON, logger.format)
}
Test Double قسم کا انتخاب اس بات پر منحصر ہے کہ اصل میں کیا ٹیسٹ کیا جا رہا ہے: حالت، رویہ، یا انضمام۔ Android اور iOS موبائل ڈویلپمنٹ میں، درج ذیل سفارشات قائم کی گئی ہیں۔
ViewModel کی جانچ کرتے وقت، ان انحصاروں کے لیے Mock استعمال کریں جو مضر اثرات پیدا کرتے ہیں (ریپوزٹریز، اینالیٹکس، نیویگیشن) اور ان انحصاروں کے لیے Stub جو ڈیٹا لوٹاتے ہیں (API کلائنٹس، ContentProvider)۔ یہ تصدیق کرنے کی اجازت دیتا ہے کہ ViewModel کامیابی اور غلطی دونوں منظرناموں کو صحیح طریقے سے ہینڈل کرتا ہے۔
Repository سطح پر، Fake (میموری میں ڈیٹا بیس کے نفاذ) اور Stub (مقررہ API جوابات) کو ترجیح دیں۔ Fake SQLite ترتیب دیے بغیر کیشنگ منطق اور آف لائن موڈ کی جانچ کرنے کی اجازت دیتا ہے۔ Stub مختلف HTTP حالات کی نقل کرتا ہے: 200، 404، 500، timeout۔
Test Doubles کا غلط استعمال نازک ٹیسٹوں کی سب سے عام وجوہات میں سے ایک ہے جو ہر ری فیکٹرنگ کے ساتھ ٹوٹ جاتے ہیں۔
سب سے عام غلطی ہر چیز کو موک کرنا ہے۔ اگر ٹیسٹ میں ہر انحصار کو Mock سے بدل دیا جائے تو ٹیسٹ حقیقی رویے کی تصدیق کرنا چھوڑ دیتا ہے۔ Mock صرف بیرونی انحصاروں کے لیے استعمال ہونا چاہیے (نیٹ ورک، ڈیٹا بیس، فائل سسٹم، سسٹم سروسز)۔ اندرونی ایپلیکیشن اجزاء (Value Object، data class، سادہ افادیتیں) تبدیل نہیں ہونے چاہئیں۔
دوسری غلطی توقعات کی وضاحت کیے بغیر Mock بنانا ہے۔ اگر کوئی طریقہ every / when کے بغیر بلایا جائے تو Mock ایک طے شدہ قدر لوٹاتا ہے (null، 0، false)۔ یہ جھوٹے مثبت ٹیسٹوں کا باعث بن سکتا ہے، جہاں Mock خاموشی سے null لوٹاتا ہے اور ٹیسٹ اسے صحیح رویے سے تعبیر کرتا ہے۔
تیسری غلطی ہر Mock کی ہر کال کی تصدیق کرنا ہے۔ Verify صرف ان کالوں کے لیے استعمال ہونا چاہیے جو کاروباری منطق کے نقطہ نظر سے اہم ہوں۔ ضرورت سے زیادہ تصدیق ٹیسٹوں کو نازک بناتی ہے: پروڈکشن کوڈ میں کالوں کی ترتیب کو تبدیل کرنا رویے کو تبدیل کیے بغیر ٹیسٹوں کو توڑ دیتا ہے۔
اکثر پوچھے گئے سوالات
Stub ڈیٹا لوٹاتا ہے اور حالت (کیا لوٹایا گیا) کی تصدیق کرتا ہے، جبکہ Mock رویے (کون سے طریقے بلائے گئے) کی تصدیق کرتا ہے۔ Stub = «X لوٹاؤ»، Mock = «تصدیق کرو کہ Y کو دلیل Z کے ساتھ بلایا گیا»۔ حقیقی ٹیسٹوں میں، ایک شے اکثر ایک ساتھ Stub اور Mock دونوں کے طور پر کام کرتی ہے۔
Fake Mock سے بہتر ہے جب حالت پر منحصر منطق کی جانچ کی جائے: کیشنگ، آف لائن موڈ، لین دین۔ Fake (میموری میں نفاذ) نازک verify کالوں کے بغیر ان منظرناموں کی جانچ کرنے کی اجازت دیتا ہے۔ Mock ڈیٹا بھیجنے کی تصدیق کے لیے زیادہ موزوں ہے: اینالیٹکس، push، ای میل۔
Kotlin میں Android منصوبوں کے لیے MockK تجویز کیا جاتا ہے۔ یہ اضافی ترتیب کے بغیر coroutines، suspend فنکشنز، sealed کلاسز اور extension فنکشنز کو سپورٹ کرتا ہے۔ Java منصوبوں کے لیے Mockito معیار ہے — وسیع دستاویزات کے ساتھ سب سے مشہور لائبریری۔
Kotlin Flow کو ٹیسٹ کرنے کے لیے MockK کے ساتھ Turbine لائبریری استعمال کریں۔ Turbine Flow کی اخراج کی تصدیق کو آسان بناتی ہے: آپ اقدار کی ترتیب، سٹریم کی تکمیل اور مستثنیات کی تصدیق کر سکتے ہیں۔ Flow کے لیے Stub flowOf(value) لوٹاتا ہے، Mock تصدیق کرتا ہے کہ Flow جمع کیا گیا تھا۔
ہاں، لیکن API جوابات کی سطح پر، UI اجزاء کی نہیں۔ MockWebServer (OkHttp) اور WireMock لائبریریاں UI ٹیسٹوں میں HTTP جوابات کو تبدیل کرنے کی اجازت دیتی ہیں۔ UI اجزاء خود (Compose، SwiftUI Views) تبدیل نہیں ہونے چاہئیں — ان کے رویے کی جانچ اسکرین شاٹ ٹیسٹوں اور Espresso کے ذریعے کی جاتی ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں