Spy (جاسوس) — ایک ٹیسٹ آبجیکٹ جو حقیقی مثال کو لپیٹتا ہے اور ہر کال کے بارے میں معلومات ریکارڈ کرتا ہے: کون سے میتھڈ کال کیے گئے، کن آرگیومینٹس کے ساتھ، اور کتنی بار۔ mock کے برعکس، spy لپیٹے گئے آبجیکٹ کی حقیقی عملدرآمد استعمال کرتا ہے — کالیں حقیقی کوڈ سے گزرتی ہیں، اور spy صرف حقائق ریکارڈ کرتا ہے۔ ٹیسٹ کے عمل کے بعد، ڈویلپر جاسوس کے ریکارڈ چیک کرتا ہے: «کیا sendAnalytics میتھڈ تین بار کال کیا گیا تھا؟»۔ مزید معلومات کے لیے Android ٹیسٹنگ گائیڈ دیکھیں۔
اہم نکات
Spy — ایک حقیقی آبجیکٹ کے گرد ایک لپیٹنے والا ہے جو تمام میتھڈ کالز کو روکتا ہے اور انہیں ریکارڈ کرتا ہے۔ آبجیکٹ کی حقیقی منطق عمل میں آتی ہے: اگر میتھڈ ڈیٹا محفوظ کرتا ہے، قدر کا حساب لگاتا ہے، یا درخواست کرتا ہے — سب کچھ معمول کے مطابق ہوتا ہے۔ اضافی طور پر، spy میٹا ڈیٹا ریکارڈ کرتا ہے: میتھڈ کا نام، آرگیومینٹس، کال کی تعداد، عمل کا وقت۔ یہ اصطلاح Meszaros (2007) کی درجہبندی کا حصہ ہے اور Martin Fowler کے مضمون “Mocks Aren’t Stubs” میں تفصیل سے بیان کی گئی ہے۔
Mock سے بنیادی فرق — mock آبجیکٹ کو مکمل طور پر ٹیسٹ stub سے بدل دیتا ہے؛ تمام میتھڈ ڈیفالٹ طور پر کچھ نہیں کرتے۔ spy موجودہ آبجیکٹ کو لپیٹتا ہے: تمام میتھڈ ڈیفالٹ طور پر معمول کے مطابق کام کرتے ہیں، لیکن ریکارڈ بھی ہوتے ہیں۔ یہ فرق بنیادی ہے: mock ٹیسٹ شدہ کوڈ کو حقیقت سے الگ کرتا ہے، جبکہ spy حقیقت کو محفوظ رکھتا ہے اور اسے مشاہدہ کرنے کی اجازت دیتا ہے۔ ان کے درمیان انتخاب اس بات پر منحصر ہے کہ کیا ٹیسٹ کیا جا رہا ہے۔
Spy صحیح انتخاب ہے — اگر ٹیسٹ شدہ کوڈ کسی حقیقی آبجیکٹ کی حالت تبدیل کرتا ہے، اور ٹیسٹ کو نتیجہ (حالت) کی تصدیق کرنی ہو اور یہ یقینی بنانا ہو کہ کالیں صحیح ترتیب میں کی گئی تھیں۔ mock مناسب نہیں ہے کیونکہ یہ حقیقی عملدرآمد نہیں کرتا۔ stub مناسب نہیں ہے کیونکہ یہ کالز ریکارڈ نہیں کرتا۔ Spy واحد ٹیسٹ ڈبل ہے جو بیک وقت حقیقی منطق کو محفوظ رکھتا ہے اور کالز کے بارے میں معلومات فراہم کرتا ہے۔
Mock — مکمل علیحدگی۔ اگر ٹیسٹ کو حقیقی آبجیکٹ کے عملدرآمد پر منحصر نہیں ہونا چاہیے (مثال کے طور پر، ڈیٹابیس یا نیٹ ورک کلائنٹ)، mock استعمال کریں۔ mock اس بات کی ضمانت دیتا ہے کہ کوئی کال حقیقی جزو تک نہیں پہنچتی۔ یہ محفوظ اور پیشقابل ہے۔ نقصان: mock حقیقی منطق نہیں چلاتا، لہذا اگر ٹیسٹ شدہ کوڈ کسی واپسی قدر پر منحصر ہے، تو اسے when/stub کے ذریعے واضح طور پر ترتیب دینا ہوگا۔
Spy — حقیقی منطق + مشاہدہ۔ اگر ٹیسٹ شدہ کوڈ کسی ایسے آبجیکٹ کے ساتھ تعامل کرتا ہے جس کی منطق ٹیسٹ کے لیے اہم ہے، نہ کہ صرف ڈیٹا — spy استعمال کریں۔ مثال کے طور پر، AnalyticsTracker جو ایونٹس جمع کرتا ہے اور وقتاً فوقتاً بھیجتا ہے۔ ٹیسٹ تصدیق کرتا ہے کہ ایونٹس بفر میں شامل کیے گئے، اور بھیجنے کے بعد بفر صاف ہو گیا۔ mock اس کی تصدیق نہیں کر سکتا کیونکہ یہ ٹریکر کی حقیقی منطق نہیں چلاتا۔
| منظرنامہ | Spy | Mock |
|---|---|---|
| حقیقی منطق ضروری | ہاں | نہیں (stub) |
| کال کی تصدیق | ہاں (تعداد، آرگیومینٹس) | ہاں (تعداد، آرگیومینٹس) |
| جزوی stubbing | ہاں (کچھ میتھڈ spy، دیگر stub) | نہیں (تمام میتھڈ stub) |
| ضمنی اثرات کا خطرہ | زیادہ (حقیقی کوڈ) | صفر |
| رفتار | کم (حقیقی منطق) | زیادہ (stub) |
| پڑھنے کی اہلیت | کم (یہ سمجھنا مشکل کہ کیا حقیقی ہے) | زیادہ (سب کچھ واضح ہے) |
ہر چیز کے لیے spy — تمام ٹیسٹوں میں mock کے بجائے spy استعمال کرنا ایک غلطی ہے۔ spy حقیقی کوڈ چلاتا ہے جس کے ضمنی اثرات ہو سکتے ہیں: فائل میں لکھنا، HTTP بھیجنا، عالمی حالت تبدیل کرنا۔ اگر ٹیسٹ شدہ ماڈیول کسی spy آبجیکٹ کے میتھڈ کو کال کرتا ہے جو HTTP درخواست کرتا ہے، تو ٹیسٹ یونٹ ٹیسٹ نہیں بلکہ انٹیگریشن ٹیسٹ بن جاتا ہے۔ اصول: اگر spy I/O آپریشنز والے آبجیکٹ کو لپیٹتا ہے — تو یہ اب یونٹ ٹیسٹ نہیں رہا۔ I/O کو الگ کرنے کے لیے mock استعمال کریں، اور spy صرف میموری میں موجود آبجیکٹس کے لیے استعمال کریں جن کے بیرونی اثرات نہ ہوں۔
Mockito.spy() — Java/Kotlin پروجیکٹس میں جاسوس بنانے کا کلاسک طریقہ۔ spy() ایک حقیقی آبجیکٹ لیتا ہے اور ایک لپیٹنے والا لوٹاتا ہے۔ تمام کالیں ڈیفالٹ طور پر حقیقی آبجیکٹ کو سونپی جاتی ہیں اور نتائج ریکارڈ کیے جاتے ہیں۔ ٹیسٹ کے بعد، آپ کال کی تعداد اور آرگیومینٹس چیک کرنے کے لیے verify() استعمال کر سکتے ہیں۔ ان میتھڈز کے لیے جو ٹیسٹ ڈیٹا واپس کرنی چاہیے، doReturn/when استعمال کیا جاتا ہے — اسے “جزوی mocking” کہا جاتا ہے۔
class AnalyticsReporterTest {
private val realTracker = AnalyticsTracker()
private val spyTracker = Mockito.spy(realTracker)
fun test_event_tracked() {
val event = AnalyticsEvent("login")
spyTracker.track(event)
Mockito.verify(spyTracker).track(event)
assertEquals(1, spyTracker.getBufferedCount())
}
fun test_track_with_exception() {
Mockito.doThrow(RuntimeException("network"))
.when(spyTracker).flush()
spyTracker.track(AnalyticsEvent("login"))
assertTrue(spyTracker.hasPendingEvents())
}
}
MockK.spyk() — coroutine اور sealed کلاسز کے لیے بہتر سپورٹ کے ساتھ Kotlin پروجیکٹس کا ایک متبادل۔ MockK.spyk() ایک جاسوس بناتا ہے، جو Mockito.spy() کے مشابہ ہے۔ یہ suspend فنکشنز کے لیے coVerify اور جزوی stubbing کے لیے every کو سپورٹ کرتا ہے۔ Mockito کے برعکس، MockK final کلاسز کے لیے spy کو سپورٹ نہیں کرتا (Kotlin میں تمام کلاسز ڈیفالٹ طور پر final ہوتی ہیں) — آپ کو کلاس کھولنی (open) یا انٹرفیس استعمال کرنا ہوگا۔
class LoginUseCaseTest {
private val realRepo = UserRepository()
private val spyRepo = spyk(realRepo)
private val useCase = LoginUseCase(spyRepo)
fun test_login_calls_save() = runTest {
every { spyRepo.getUser(any()) } returns User("test")
val result = useCase.login("test", "pass")
coVerify { spyRepo.saveLoginTime(any()) }
assertTrue(result.isSuccess)
}
}
جزوی stubbing — ایک طاقتور لیکن خطرناک تکنیک۔ آپ کسی آبجیکٹ کا spy بنا سکتے ہیں اور صرف کچھ میتھڈز کو اوور رائڈ (stub) کر سکتے ہیں، باقی کو حقیقی چھوڑ سکتے ہیں۔ مثال: ایک spy ریپوزٹری جہاں getUser() ٹیسٹ ڈیٹا واپس کرتا ہے، جبکہ saveUser() حقیقت میں میموری میں موجود فہرست میں محفوظ کرتا ہے۔ یہ stub (کنٹرول شدہ ڈیٹا) اور spy (حقیقی منطق) دونوں کے فوائد کو یکجا کرنے کی اجازت دیتا ہے۔ نقصان: ٹیسٹ کی پڑھنے کی اہلیت متاثر ہوتی ہے — یہ واضح نہیں ہوتا کہ کون سے میتھڈ حقیقی ہیں اور کون سے stub۔
Objective-C کے لیے OCMock — ایک لائبریری جو niceMock کے ذریعے spy آبجیکٹس بنانے کی حمایت کرتی ہے۔ OCMock Objective-C رن ٹائم استعمال کرکے میتھڈ کالز کو روکتا ہے اور انہیں ریکارڈ کرتا ہے۔ ٹیسٹ کے بعد verify کہا جاتا ہے۔ OCMock کسی بھی آبجیکٹ کے لیے spy کو سپورٹ کرتا ہے (Objective-C میں تمام میتھڈ ڈائنامک ہوتے ہیں)، جو اسے Swift پر برتری دیتا ہے، جہاں spy صرف پروٹوکول کے ذریعے ممکن ہوتے ہیں۔
// حقیقی آبجیکٹ کے لیے spy بنانا
AnalyticsTracker *realTracker = [[AnalyticsTracker alloc] init];
AnalyticsTracker *spy = [OCMockObject partialMockForObject:realTracker];
// ٹیسٹ چلانا
[spy trackEvent:@"login"];
// تصدیق
[[spy verify] trackEvent:@"login"];
XCTAssertEqual([realTracker eventCount], 1);
Swift پروٹوکول پر مبنی spy — Swift میں Objective-C جیسا رن ٹائم ریفلیکشن نہیں ہے، لہذا spy دستی طور پر بنائے جاتے ہیں۔ ایک ٹیسٹ ڈھانچہ ایک پروٹوکول کو نافذ کرتا ہے اور اندرونی طور پر حقیقی آبجیکٹ کو کال کرتا ہے جبکہ بیک وقت کالز ریکارڈ کرتا ہے۔ اس کے لیے زیادہ کوڈ درکار ہے، لیکن یہ مکمل طور پر کنٹرول قابل اور ٹائپ محفوظ ہے۔ دستی جاسوسوں کو بیرونی لائبریریوں کی ضرورت نہیں ہوتی اور وہ رن ٹائم استعمال نہیں کرتے — سب کچھ کمپائل ٹائم پر چیک کیا جاتا ہے۔
protocol AnalyticsProtocol {
func trackEvent(name: String)
}
final class SpyAnalytics: AnalyticsProtocol {
private let real: AnalyticsProtocol
private var events: [String] = []
init(real: AnalyticsProtocol) {
self.real = real
}
func trackEvent(name: String) {
events.append(name)
real.trackEvent(name: name)
}
func verifyTracked(name: String) -> Bool {
return events.contains(name)
}
}
OCMock بمقابلہ دستی spy کب استعمال کریں — Objective-C کوڈ کے لیے OCMock استعمال کریں (کم بوائلرپلیٹ)۔ Swift کے لیے پروٹوکول کے ذریعے دستی جاسوس ترجیح دیتے ہیں۔ ایک دستی spy کال ریکارڈنگ پر مکمل کنٹرول دیتا ہے، ریفلیکشن کی ضرورت نہیں ہوتی اور ویلیو ٹائپس (struct) کے ساتھ کام کرتا ہے۔ واحد نقصان یہ ہے کہ آپ کو نئے میتھڈ شامل کرتے وقت spy کلاس کوڈ کو پروٹوکول کے ساتھ ہم آہنگ رکھنا ہوگا۔
تجزیہ کی تصدیق — جاسوسوں کا سب سے عام استعمال۔ پروڈکشن کوڈ میں، تجزیہ کالز پوری ایپلیکیشن میں پھیلی ہوتی ہیں: لاگ ان، لاگ آؤٹ، خریداری، خرابی۔ ٹیسٹ AnalyticsTracker کے لیے ایک spy لپیٹنے والا بناتا ہے، ایک منظرنامہ انجام دیتا ہے (لاگ ان، پروڈکٹ دیکھنا، کارٹ میں شامل کرنا، خریدنا) اور چیک کرتا ہے کہ تمام ضروری ایونٹس صحیح ترتیب میں بھیجے گئے۔ mock مناسب نہیں ہے کیونکہ AnalyticsTracker میں بفرنگ اور بھیجنے کی منطق ہے۔
ٹائمر اور شیڈیولر — Handler (Android) یا Timer (iOS) استعمال کرنے والے کوڈ کی جانچ حقیقی وقت کی وجہ سے مشکل ہے۔ Scheduler کے لیے ایک spy لپیٹنے والا ریکارڈ کرتا ہے کہ کون سے کام کس تاخیر سے طے کیے گئے۔ ٹیسٹ حقیقی Handler کا spy بناتا ہے، ایک عمل کرتا ہے، اور چیک کرتا ہے کہ Handler.postDelayed(runnable, delay) صحیح تاخیر سے کال ہوا۔ حقیقی کام انجام نہیں دیا جاتا — spy کال کو روکتا اور ریکارڈ کرتا ہے۔
لاگنگ اور ڈیبگ معلومات — پروڈکشن میں، لاگ غیر فعال یا فائل میں لکھے جا سکتے ہیں۔ Logger کے لیے ایک spy لپیٹنے والا تمام پیغامات کو میموری میں موجود فہرست میں ریکارڈ کرتا ہے جسے ٹیسٹ عمل کے بعد چیک کرتا ہے۔ یہ کنسول کو بے ترتیب کیے بغیر خرابی کی صورت میں صحیح پیغام لکھے جانے کی تصدیق کرنے کی اجازت دیتا ہے۔ Logger کے لیے دستی جاسوس iOS پر خاص طور پر مفید ہیں، جہاں OSLog کے پاس ٹیسٹ API نہیں ہے۔
کال ترتیب کی تصدیق — کچھ منظرناموں کو آپریشنز کی سخت ترتیب درکار ہوتی ہے: کنکشن کھولیں، ڈیٹا بھیجیں، کنکشن بند کریں۔ Mockito InOrder.verify() کے ذریعے ترتیب چیک کرنے کی اجازت دیتا ہے۔ spy بھی ایسا ہی کرتا ہے لیکن حقیقی عمل کو محفوظ رکھتا ہے۔ اگر ترتیب اور ہر مرحلے کا نتیجہ (کنکشن واقعی کھلا) دونوں اہم ہوں — mock نہیں، spy استعمال کریں۔
اکثر پوچھے گئے سوالات
Spy ایک حقیقی آبجیکٹ کو لپیٹتا ہے اور اس کی منطق چلاتا ہے، ساتھ ہی کالز ریکارڈ کرتا ہے۔ Mock آبجیکٹ کو مکمل طور پر stub سے بدل دیتا ہے — کوئی حقیقی منطق عمل نہیں ہوتی۔ spy رویہ محفوظ رکھتا ہے، mock نہیں رکھتا۔ جب آبجیکٹ کا حقیقی کام اہم ہو تو spy منتخب کریں؛ جب آپ کو ٹیسٹ کو بیرونی انحصار سے الگ کرنے کی ضرورت ہو تو mock منتخب کریں۔
جب spy لپیٹنے والا حقیقی I/O آپریشنز کا باعث بنتا ہے۔ اگر spy کسی ایسے آبجیکٹ کو لپیٹتا ہے جو فائل میں لکھتا ہے، HTTP بھیجتا ہے، یا ڈسک سے پڑھتا ہے — ٹیسٹ یونٹ ٹیسٹ نہیں رہتا۔ دوسری صورت: ٹیسٹ صرف واپسی قدر چیک کرتا ہے کالز میں دلچسپی لیے بغیر — یہاں stub کافی ہے اور spy بے کار ہے۔ تیسری: کوڈ spy کی اندرونی حالت پر منحصر ہے — یہ ایک نازک ٹیسٹ ہے۔
ہاں، spyk() کے ذریعے — Mockito.spy() کے مساوی۔ MockK.spyk() ایک حقیقی آبجیکٹ کے گرد جاسوس بناتا ہے، جزوی stubbing کے لیے every اور suspend فنکشنز کے لیے coVerify/coroutinesVerify کو سپورٹ کرتا ہے۔ حد: final کلاسز کے ساتھ کام نہیں کرتا (open یا انٹرفیس درکار ہے)۔ Java کلاسز کے لیے، MockK spyk() کو بھی سپورٹ کرتا ہے لیکن @MockKJvmInline انوٹیشن درکار ہے۔
تکنیکی طور پر، نہیں۔ Mock ایک stub ہے جس میں کوئی حقیقی عملدرآمد نہیں ہے۔ Spy، تعریف کے مطابق، ایک حقیقی آبجیکٹ کو لپیٹتا ہے۔ Mockito میں، آپ mock کو spy میں تبدیل نہیں کر سکتے۔ لیکن آپ الٹ کر سکتے ہیں: ایک spy بنائیں اور doReturn/when (جزوی mocking) کے ذریعے کچھ میتھڈز کو اوور رائڈ کریں۔ یہ spy آبجیکٹ کے منتخب میتھڈز کے لیے mock جیسا رویہ دیتا ہے۔
ہاں، ضروری ہے۔ Swift میں Java/Kotlin جیسا ڈائنامک پراکسی نہیں ہے۔ spy بنانے کے لیے ایک پروٹوکول درکار ہے جسے پروڈکشن کلاس اور spy کلاس دونوں نافذ کریں۔ Swift پروٹوکول پر مبنی spy ایک دستی نفاذ ہے جو ایک حقیقی آبجیکٹ لیتا ہے، اسے کالز سونپتا ہے، اور میٹا ڈیٹا ریکارڈ کرتا ہے۔ متبادل: Cuckoo لائبریری، جو SourceKit کے ذریعے spy کلاسز تیار کرتی ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں