क्या आप सुनिश्चित हैं कि आपके टेस्ट वास्तव में काम करते हैं?
\n\nकल्पना करें: आपके पास 95% कोड कवरेज है, सभी टेस्ट हरे हैं, CI/CD बिना किसी त्रुटि के गुजरता है। टीम खुश है, मैनेजर शांत है। लेकिन एक दिन प्रोडक्शन में एक बग सामने आता है। आप कोड खोलते हैं, टेस्ट देखते हैं और समझते हैं: वे बस जाँच कर रहे हैं कि फंक्शन एक संख्या लौटाता है, लेकिन उसके मान को नहीं देखते। या इससे भी बुरा — टेस्ट इस तरह लिखा गया है कि वह किसी भी स्थिति में पास हो जाता है।
\n\nपरिचित स्थिति? उस दुनिया में आपका स्वागत है जहाँ कवरेज का प्रतिशत सिर्फ एक आंकड़ा है, गुणवत्ता की गारंटी नहीं। यहाँ पर मंच पर आता है म्यूटेशन टेस्टिंग (mutation testing) — एक ऐसी विधि जो वास्तव में आपके टेस्ट को कष्ट देती है और उन्हें अपनी क्षमता साबित करने के लिए मजबूर करती है।
\n\nइस लेख में, हम देखेंगे कि Stryker का उपयोग करके TypeScript प्रोजेक्ट में mutation testing कैसे लागू करें, और यह आपका नया पसंदीदा टूल (या सिरदर्द का नया स्रोत) क्यों बन सकता है।
\n\nसरल शब्दों में म्यूटेशन टेस्टिंग क्या है?
\n\nम्यूटेशन टेस्टिंग वह है जब आप अपना कोड लेते हैं, जानबूझकर उसमें छोटी-छोटी त्रुटियाँ (म्यूटेशन) डालते हैं और देखते हैं कि क्या आपके टेस्ट फेल होते हैं। यदि टेस्ट फेल नहीं होते — तो इसका मतलब है कि आप गलत तरीके से टेस्ट कर रहे हैं। यदि वे फेल होते हैं — तो आप अच्छे हैं, लेकिन म्यूटेंट मारा गया।
\n\nयह सैडिज़्म जैसा लगता है? शायद। लेकिन यह काम करता है। कल्पना करें कि आप एक QA-इंजीनियर हैं, जो चुपके से कोड में घुस गया और === को ==, > को < से बदल दिया या फंक्शन कॉल हटा दिया। एक अच्छे टेस्ट को इसे नोटिस करना चाहिए। एक खराब टेस्ट इसे छोड़ देगा।
TypeScript में, अपनी सख्त टाइपिंग के साथ, म्यूटेशन टेस्टिंग विशेष रूप से उपयोगी है: यह उन मामलों को पकड़ता है जहाँ टाइप सही हैं, लेकिन लॉजिक नहीं है।
\n\nसामान्य कोड कवरेज (code coverage) एक भ्रम क्यों है?
\n\nमान लीजिए, आपके पास एक फंक्शन है:
\n\nfunction calculateDiscount(price: number, isPremium: boolean): number {\n if (isPremium) {\n return price * 0.8;\n }\n return price;\n}\n\nऔर एक टेस्ट:
\n\ntest('returns number', () => {\n const result = calculateDiscount(100, true);\n expect(typeof result).toBe('number');\n});\n\nकवरेज? 100%। टेस्ट? पास हो जाता है। क्या होगा अगर हम price * 0.8 को price * 0.5 से बदल दें? टेस्ट फिर भी पास हो जाएगा — वह तो सिर्फ टाइप की जाँच कर रहा है, मान की नहीं। यह रहा आपका सुरक्षा का झूठा एहसास।
म्यूटेशन टेस्टिंग ऐसे म्यूटेशन को उजागर करेगी और कहेगी: "अरे, तुम्हारे टेस्ट ने ध्यान नहीं दिया कि मैंने डिस्काउंट बदल दिया!" और आपको एक सामान्य जाँच लिखनी होगी।
\n\nTypeScript में mutation testing कैसे काम करता है: चरण-दर-चरण
\n\nपूरी प्रक्रिया को तीन चरणों में विभाजित किया जा सकता है:
\n\n- \n
- म्यूटेंट का निर्माण — टूल आपके कोड की प्रतिलिपि बनाता है और एक बार में एक बदलाव करता है। उदाहरण के लिए,
&&को||से बदलना या मेथड कॉल को हटाना। \n - टेस्ट चलाना — प्रत्येक म्यूटेंट के लिए आपके टेस्ट चलाए जाते हैं। \n
- परिणामों का विश्लेषण — यदि टेस्ट फेल होते हैं, तो म्यूटेंट "मारा गया"। यदि नहीं, तो वह "बच गया", और यह एक जोखिम क्षेत्र है। \n
मुख्य मीट्रिक है mutation score (मारे गए म्यूटेंट का प्रतिशत)। 100% आदर्श है, लेकिन व्यवहार में 80-90% पहले से ही बहुत अच्छा है।
\n\nTypeScript के लिए टूल #1: Stryker
\n\nTypeScript और JavaScript की दुनिया में म्यूटेशन टेस्टिंग का राजा है Stryker (StrykerJS)। यह तेज़ है, Jest, Vitest, Mocha और यहाँ तक कि ब्राउज़र टेस्ट के साथ भी काम कर सकता है। इसे 5 मिनट में कॉन्फ़िगर किया जा सकता है।
\n\nइंस्टॉल करें:
\n\nnpm install --save-dev @stryker-mutator/core @stryker-mutator/jest-runner\n\nकॉन्फ़िग फ़ाइल stryker.conf.js बनाएँ:
// stryker.conf.js\nexport default {\n mutate: ['src/**/*.ts', '!src/**/*.spec.ts'], // क्या म्यूटेट करना है\n testRunner: 'jest',\n reporters: ['html', 'clear-text', 'progress'],\n jest: {\n projectType: 'custom',\n configFile: 'jest.config.ts',\n },\n concurrency: 4, // कितने म्यूटेंट