परिचय: मॉक के बिना परीक्षण कैफीन के बिना कॉफी के समान क्यों है?
\n\nनमस्ते, सहकर्मी! यदि आप Kotlin में लिखते हैं और चाहते हैं कि आपका कोड हर दूसरे कमिट पर न टूटे, तो देर-सबेर आप परीक्षणों तक पहुँचेंगे। और परीक्षणों में - मॉक-ऑब्जेक्ट और फिक्स्चर तक। उनके बिना, यूनिट परीक्षण इंटीग्रेशन परीक्षणों में बदल जाते हैं: आप एक वर्ग का नहीं, बल्कि निर्भरताओं की एक पूरी श्रृंखला का परीक्षण कर रहे होते हैं। और यदि डेटाबेस डाउन हो जाता है या API 500 लौटाता है - तो परीक्षण विफल हो जाएगा, भले ही आपका कोड सही हो।
\n\nइस लेख में, हम देखेंगे कि Kotlin परीक्षणों में मॉक-ऑब्जेक्ट और फिक्स्चर का सही तरीके से उपयोग कैसे करें, ताकि परीक्षण तेज़, विश्वसनीय हों और आपको सच बताएँ, न कि "सब कुछ टूट गया है, ठीक करो"। चलिए शुरू करते हैं!
\n\nमॉक क्या है और Kotlin में इसकी आवश्यकता क्यों है?
\n\nमॉक-ऑब्जेक्ट एक वास्तविक ऑब्जेक्ट का अनुकरण है। पूरे सर्वर या डेटाबेस को चालू करने के बजाय, हम एक "स्टब" बनाते हैं जो हमारे कहने के अनुसार व्यवहार करता है। यह परीक्षण किए जा रहे कोड को बाहरी दुनिया से अलग करने की अनुमति देता है।
\n\nमुख्य अवधारणाएँ
\n- \n
- Mock — एक ऑब्जेक्ट जिसके तरीकों के व्यवहार को हम नियंत्रित करते हैं। हम निर्धारित कर सकते हैं कि वह क्या लौटाएगा, और जाँच सकते हैं कि उसे कॉल किया गया था या नहीं। \n
- Stub — मॉक का एक विशेष मामला जो केवल पूर्व-निर्धारित मान लौटाता है। यह कॉल की जाँच नहीं करता है। \n
- Fixture (फिक्स्चर) — पूर्व-तैयार डेटा या स्थितियों का एक सेट जो परीक्षण को निष्पादित करने के लिए आवश्यक है। उदाहरण के लिए, एक परीक्षण उपयोगकर्ता या DB सेटिंग्स। \n
Kotlin की दुनिया में दो मुख्य खिलाड़ी हैं: MockK (Kotlin के लिए मूल) और Mockito (Java में लोकप्रिय)। हम MockK का उपयोग करेंगे - यह कोरूटीन, एक्सटेंशन फ़ंक्शन और sealed क्लास के साथ बेहतर काम करता है।
\n\nप्रोजेक्ट सेटअप: MockK और JUnit 5 जोड़ना
\n\nशुरू करने के लिए, build.gradle.kts में निर्भरताएँ जोड़ें:
dependencies {\n testImplementation("io.mockk:mockk:1.13.8")\n testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")\n}\n\n\nअब परीक्षण लिखे जा सकते हैं। आइए एक सरल सेवा बनाएँ और मॉक का उपयोग करके इसका परीक्षण करें।
\n\nपहला उदाहरण: रिपॉजिटरी को मॉक करना
\n\nकल्पना करें कि हमारे पास UserService है, जो UserRepository पर निर्भर करता है:
class UserService(private val repository: UserRepository) {\n fun getUserName(id: Long): String {\n val user = repository.findById(id)\n ?: throw IllegalArgumentException("User not found")\n return user.name\n }\n}\n\n\nअब रिपॉजिटरी के लिए mock-ऑब्जेक्ट का उपयोग करके एक परीक्षण लिखें:
\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport kotlin.test.assertEquals\nimport kotlin.test.assertFailsWith\n\nclass UserServiceTest {\n\n private val repository = mockk<UserRepository>()\n private val service = UserService(repository)\n\n @Test\n fun `should return user name when user exists`() {\n // Arrange\n every { repository.findById(1L) } returns User(1L, "Alice")\n\n // Act\n val name = service.getUserName(1L)\n\n // Assert\n assertEquals("Alice", name)\n verify { repository.findById(1L) }\n }\n\n @Test\n fun `should throw exception when user not found`() {\n // Arrange\n every { repository.findById(any()) } returns null\n\n // Act & Assert\n assertFailsWith<IllegalArgumentException> {\n service.getUserName(99L)\n }\n }\n}\n\n\nध्यान दें: every मॉक के व्यवहार को सेट करता है, verify जाँचता है कि विधि को कॉल किया गया था। यह Kotlin परीक्षणों में मॉक-ऑब्जेक्ट और फिक्स्चर का आधार है।
फिक्स्चर: डेटा पहले से तैयार करना
\n\nजब बहुत सारे परीक्षण होते हैं, तो डेटा बनाने की नकल करना एक बुरी प्रथा है। यहाँ फिक्स्चर मदद करते हैं। Kotlin में, उन्हें सुविधाजनक रूप से