Mock एक विकल्पी ऑब्जेक्ट है जो वास्तविक घटक के व्यवहार का अनुकरण करता है और इसके साथ इंटरैक्शन की जाँच करने की अनुमति देता है। Stub के विपरीत, जो केवल एक पूर्वनिर्धारित मान लौटाता है, Mock विधि के आह्वान, पारित किए गए आर्गुमेंट और कॉल की संख्या को रिकॉर्ड करता है। Mockito (2024) के अनुसार, Mock Java और Kotlin प्रोजेक्ट्स में सबसे लोकप्रिय Test Double प्रकार है, जिसका उपयोग मोबाइल एप्लिकेशन के 70% से अधिक यूनिट टेस्ट में किया जाता है।
मुख्य बिंदु
Mock एक ऑब्जेक्ट है जो मॉकिंग फ्रेमवर्क (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 के अनुसार, Mock यह जाँचने का एकमात्र तरीका है कि किसी एप्लिकेशन ने वास्तव में टेस्ट सर्वर चालू किए बिना 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 की बदौलत फ़ाइनल क्लास, स्टैटिक विधियों और कंस्ट्रक्टर के लिए मॉकिंग का समर्थन करता है। Kotlin प्रोजेक्ट्स के लिए, Mockito को अतिरिक्त सेटअप की आवश्यकता होती है: बेहतर सिंटैक्स के लिए mockito-kotlin एक्सटेंशन, फ़ाइनल क्लास के लिए mockito-inline। Mockito अतिरिक्त एडॉप्टर के बिना Kotlin कोरूटीन और suspend फ़ंक्शन का समर्थन नहीं करता।
MockK विशेष रूप से Kotlin के लिए बनाया गया था। यह मूल रूप से कोरूटीन (coEvery, coVerify), सील्ड क्लास, डेटा क्लास, ऑब्जेक्ट सिंगलटन और एक्सटेंशन फ़ंक्शन का समर्थन करता है। MockK सिंटैक्स लैम्ब्डा ब्लॉक के साथ DSL का उपयोग करता है, जो Kotlin कोड में स्वाभाविक लगता है। MockK बिना अतिरिक्त सेटअप के प्रॉपर्टी मॉकिंग का भी समर्थन करता है — यह 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% तेज़ी से मॉक ऑब्जेक्ट बनाता है। हज़ारों यूनिट टेस्ट वाले प्रोजेक्ट्स के लिए, बिल्ड स्पीड में अंतर ध्यान देने योग्य हो सकता है: MockK बड़े प्रोजेक्ट्स में पूर्ण टेस्ट रन पर 30–60 सेकंड बचाता है।
तीन परिदृश्यों की जाँच करें: Mock निर्भरताओं के साथ ViewModel का परीक्षण, API कॉल सत्यापन के साथ UseCase का परीक्षण, और coVerify के साथ कोरूटीन का परीक्षण।
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) की सिफारिशों से आता है।
बुनियादी मॉकिंग के अलावा, उन्नत तकनीकें हैं जो मोबाइल डेवलपमेंट में विशिष्ट कार्यों को हल करती हैं: मल्टीथ्रेडिंग का परीक्षण, Flow स्थिति की जाँच, और वास्तविक ऑब्जेक्ट का आंशिक मॉकिंग।
Spy (या आंशिक mock) एक ऑब्जेक्ट बनाने की अनुमति देता है जो कॉल को वास्तविक कार्यान्वयन को सौंपता है लेकिन व्यक्तिगत विधियों को ओवरराइड करने देता है। MockK में, spyk एक वास्तविक क्लास इंस्टेंस के आधार पर बनाया जाता है: val repo = spyk(InMemoryUserRepository())। every के माध्यम से परिभाषित अपेक्षाओं वाले कॉल Mock के माध्यम से जाते हैं; बाकी वास्तविक ऑब्जेक्ट के माध्यम से जाते हैं। Spy विशेष रूप से लीगेसी कोड के परीक्षण के लिए उपयोगी है जहाँ डिपेंडेंसी इंजेक्शन अभी तक लागू नहीं हुआ है और आपको केवल एक विधि को ओवरराइड करने की आवश्यकता है।
Jetpack Compose का उपयोग करने वाले आधुनिक Android प्रोजेक्ट्स में, ViewModel StateFlow के माध्यम से स्थिति प्रदर्शित करता है। MockK Flow निर्भरताओं को मॉक करने की अनुमति देता है, और 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 मूल रूप से कोरूटीन का समर्थन करता है: coEvery suspend फ़ंक्शन के व्यवहार को परिभाषित करता है, coVerify कोरूटीन के अंदर इसके कॉल की जाँच करता है। सभी 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 सील्ड क्लास के साथ सही ढंग से काम करता है: every { useCase() } returns Result.Success(data)। Mockito सीधे सील्ड क्लास का समर्थन नहीं करता, जिसके लिए वर्कअराउंड की आवश्यकता होती है। यह एक कारण है कि Kotlin प्रोजेक्ट्स के लिए Mockito के बजाय MockK की सिफारिश की जाती है।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें