सीखें कि कैसे एक Claude Code टेस्ट जनरेशन प्रॉम्प्ट सीमांत, इनवेरिएंट्स और फेल्योर मोड्स पर ध्यान देकर हाई-सिग्नल टेस्ट बनाता है—happy paths के बजाय।

ऑटो-जनरेटेड टेस्ट सूट अक्सर प्रभावशाली दिखते हैं: दर्जनों टेस्ट, बहुत सारा सेटअप कोड, और हर फ़ंक्शन नाम कहीं न कहीं दिख जाता है। लेकिन उनमे से कई टेस्ट सिर्फ “जब सब कुछ सामान्य है तो यह काम करता है” वाले चेक होते हैं। ये आसानी से पास होते हैं, विरल रूप से बग पकड़ते हैं, और इन्हें पढ़ने और बनाए रखने में अभी भी समय लगता है।
एक सामान्य Claude Code टेस्ट जनरेशन प्रॉम्प्ट के साथ मॉडल दिखाए गए उदाहरण इनपुट का प्रतिबिंब बनने की प्रवृत्ति दिखाता है। आपको परिवर्तन मिलते हैं जो दिखने में अलग हैं लेकिन वही व्यवहार कवर करते हैं। परिणाम एक बड़ा सूट होता है जिसमें महत्वपूर्ण जगहों पर पतला कवरेज होता है।
हाई-सिग्नल टेस्ट अलग होते हैं। वे वही छोटे सेट होते हैं जो पिछले महीने की घटना पकड़ते। वे जोखिम वाले बदलावों पर फेल होते हैं, और बेकार रीफैक्टर के समय स्थिर रहते हैं। एक हाई-सिग्नल टेस्ट बीस “अपेक्षित मान लौटाता है” चेकों के बराबर हो सकता है।
कम-मूल्य वाले happy-path जनरेशन के सामान्य लक्षण:
कल्पना कीजिए एक फ़ंक्शन जो डिस्काउंट कोड लागू करता है। Happy-path टेस्ट पुष्टि करते हैं कि “SAVE10” कीमत घटाता है। असली बग कहीं और छिपते हैं: 0 या नकारात्मक कीमतें, समाप्त कोड, राउंडिंग किनारों, या अधिकतम डिस्काउंट कैप। ये वे केस हैं जो गलत टोटल, नाराज़ ग्राहक, और आधी रात के रोलबैक का कारण बनते हैं।
लक्ष्य "अधिक टेस्ट" से "बेहतर टेस्ट" पर जाना है—तीन लक्ष्यों को निशाना बनाकर: boundaries, failure modes, और invariants।
अगर आप हाई-सिग्नल यूनिट टेस्ट चाहते हैं, तो “और टेस्ट” माँगना बंद करें और तीन विशिष्ट प्रकार माँगना शुरू करें। यह वही कोर है एक Claude Code टेस्ट जनरेशन प्रॉम्प्ट का जो उपयोगी कवरेज पैदा करता है बजाय "सामान्य इनपुट पर काम करता है" चेक के ढेर के।
बाउंडरी वे किनारे हैं जिन्हें कोड स्वीकार करता या उत्पन्न करता है। कई वास्तविक दोष ऑफ-बाय-वन, खाली-स्थिति, या टाइमआउट समस्याएँ हैं जो कभी happy path में नहीं दिखती।
सोचें न्यूनतम और अधिकतम के संदर्भ में (0, 1, अधिकतम लंबाई), खाली बनाम मौजूद ("", [], nil), ऑफ-बाय-वन (n-1, n, n+1), और समय सीमाएँ (कटऑफ के पास)।
उदाहरण: अगर एक API “100 आइटम तक” स्वीकार करती है, तो 100 और 101 टेस्ट करें, केवल 3 नहीं।
Failure modes वे तरीके हैं जिनसे सिस्टम टूट सकता है: खराब इनपुट, गायब निर्भरताएँ, आंशिक परिणाम, या अपस्ट्रीम त्रुटियाँ। अच्छे failure mode टेस्ट तनाव के नीचे व्यवहार की जाँच करते हैं, न कि केवल आदर्श परिस्थितियों में आउटपुट।
उदाहरण: जब डेटाबेस कॉल विफल हो, क्या फ़ंक्शन एक स्पष्ट त्रुटि लौटाता है और आंशिक डेटा लिखने से बचता है?
इनवेरिएंट्स वे सत्य हैं जो कॉल से पहले और बाद में सदा सत्य बने रहने चाहिए। वे अस्पष्ट सहीपन को स्पष्ट assertions में बदल देते हैं।
उदाहरण:
इन तीन लक्ष्यों पर ध्यान देने से आपको कम लेकिन अधिक प्रभावी टेस्ट मिलते हैं।
यदि आप बहुत जल्दी टेस्ट माँगते हैं, तो आमतौर पर आपको विनम्र "अपेक्षित के अनुसार काम करता है" चेकों का ढेर मिल जाता है। एक सरल समाधान है पहले एक छोटा कॉन्ट्रैक्ट लिखना, फिर उससे टेस्ट जनरेट करना। यह Claude Code टेस्ट जनरेशन प्रॉम्प्ट को असली बग खोजने वाला बनाना का सबसे तेज़ तरीका है।
एक उपयोगी कॉन्ट्रैक्ट इतनी छोटी हो कि एक साँस में पढ़ा जा सके। 5 से 10 पंक्तियों का लक्ष्य रखें जो तीन प्रश्नों का जवाब दें: क्या इनपुट है, क्या आउटपुट आता है, और और क्या बदलता है।
कॉन्ट्रैक्ट सादा भाषा में लिखें, कोड में नहीं, और केवल वही शामिल करें जिसे आप टेस्ट कर सकते हैं।
एक बार जब आपके पास यह हो, तो स्कैन करें कि वास्तविकता कहाँ आपकी मान्यताओं को तोड़ सकती है। वे बन जाते हैं बाउंडरी केस (min/max, zero, overflow, empty strings, duplicates) और failure modes (timeouts, permission denied, unique constraint violations, corrupted input)।
यहाँ reserveInventory(itemId, qty) जैसी फीचर के लिए एक ठोस उदाहरण है:
कॉन्ट्रैक्ट कह सकता है कि qty को सकारात्मक पूर्णांक होना चाहिए, फ़ंक्शन एटोमिक होना चाहिए, और यह कभी नकारात्मक स्टॉक नहीं बनाये। इससे तुरंत हाई-सिग्नल टेस्ट सुझते हैं: qty = 0, qty = 1, qty उपलब्धता से अधिक, समवर्ती कॉल, और बीच में मजबूर डेटाबेस त्रुटि।
यदि आप Koder.ai जैसे चैट-आधारित टूल का उपयोग कर रहे हैं, तो वही वर्कफ़्लो लागू होता है: पहले कॉन्ट्रैक्ट लिखें, फिर ऐसे टेस्ट जनरेट करें जो BOUNDARIES, FAILURE MODES, और "Must never happen" सूची पर सीधे हमला करें।
जब आप कम परन्तु प्रभावी टेस्ट चाहते हैं तो यह Claude Code टेस्ट जनरेशन प्रॉम्प्ट उपयोग करें। मुख्य चाल यह है कि पहले एक टेस्ट प्लान ज़बरदस्ती कराएँ, फिर प्लान APPROVE होने के बाद ही टेस्ट कोड जनरेट करें।
You are helping me write HIGH-SIGNAL unit tests.
Context
- Language/framework: <fill in>
- Function/module under test: <name + short description>
- Inputs: <types, ranges, constraints>
- Outputs: <types + meaning>
- Side effects/external calls: <db, network, clock, randomness>
Contract (keep it small)
1) Preconditions: <what must be true>
2) Postconditions: <what must be true after>
3) Error behavior: <how failures are surfaced>
Task
PHASE 1 (plan only, no code):
A) Propose 6-10 tests max. Do not include “happy path” unless it protects an invariant.
B) For each test, state: intent, setup, input, expected result, and WHY it is high-signal.
C) Invariants: list 3-5 invariants and how each will be asserted.
D) Boundary matrix: propose a small matrix of boundary values (min/max/empty/null/off-by-one/too-long/invalid enum).
E) Failure modes: list negative tests that prove safe behavior (no crash, no partial write, clear error).
Stop after PHASE 1 and ask for approval.
PHASE 2 (after approval):
Generate the actual test code with clear names and minimal mocks.
एक व्यावहारिक चाल है boundary matrix को compact तालिका के रूप में माँगना, ताकि गैप स्पष्ट हों:
| मापदंड | वैध सीमा | ठीक बाहर | असामान्य मान | अपेक्षित व्यवहार |
|---|---|---|---|---|
| लंबाई | 0 | -1 | 10,000 | त्रुटि बनाम क्लैंप बनाम स्वीकार |
अगर Claude 20 टेस्ट सुझाता है, तो वापस धकेलें। इसे समान मामलों को मर्ज करने और केवल उन टेस्टों को रखने के लिए कहें जो असली बग पकड़ेंगे (off-by-one, गलत एरर टाइप, साइलेंट डेटा लॉस, टूटा इनवेरिएंट)।
एक छोटे, ठोस कॉन्ट्रैक्ट के साथ शुरू करें। फ़ंक्शन सिग्नेचर, इनपुट और आउटपुट का संक्षिप्त वर्णन, और किसी मौजूद टेस्ट को पेस्ट करें (भले ही वे सिर्फ happy-path हों)। इससे मॉडल को असल कोड के आस-पास एंकर रखने में मदद मिलती है।
अगला, किसी भी टेस्ट कोड माँगने से पहले एक रिस्क टेबल माँगें। तीन कॉलम आवश्यक करें: boundary cases (इन्पुट की सीमाएँ), failure modes (खराब इनपुट, गायब डेटा, टाइमआउट), और invariants (नियम जो हमेशा सही होने चाहिए)। हर पंक्ति के लिए एक वाक्य जोड़ें: "क्यों यह टूट सकता है।" एक साधारण तालिका गैप्स को अधिक तेज़ी से दिखाती है।
फिर सबसे छोटे टेस्ट सेट का चयन करें जहाँ प्रत्येक टेस्ट का एक अनूठा बग-पकड़ने वाला उद्देश्य हो। यदि दो टेस्ट एक ही कारण से फेल होंगे, तो मजबूत वाले को रखें।
एक व्यावहारिक चयन नियम:
अंत में, हर टेस्ट के लिए एक छोटा स्पष्टीकरण माँगें: यदि यह फेल होता है तो किस बग को यह पकड़ेगा। अगर स्पष्टीकरण सामान्य है ("व्यवहार को मान्य करता है"), तो टेस्ट संभवतः कम-सिग्नल का है।
इनवेरिएंट एक नियम है जो किसी भी वैध इनपुट पर भी सत्य रहना चाहिए। इनवेरिएंट-आधारित टेस्टिंग में आप पहले नियम सादा भाषा में लिखते हैं, फिर उसे एक ऐसा Assertion बनाते हैं जो ज़ोरदार तरीके से फेल कर सके।
1-2 इनवेरिएंट चुनें जो वाकई आपको असली बग से बचाते हैं। अच्छे इनवेरिएंट अक्सर सुरक्षा (कोई डेटा लॉस नहीं), संगति (एक ही इनपुट = एक ही आउटपुट), या सीमाएँ (कभी भी कैप पार न करे) के बारे में होते हैं।
इनवेरिएंट को एक छोटे वाक्य में लिखें, फिर तय करें कि आपका टेस्ट कौन से प्रमाण देख सकता है: रिटर्न वैल्यू, स्टोर किया गया डेटा, उत्सर्जित इवेंट्स, या निर्भरता कॉल। मज़बूत assertions आउटकम और साइड-इफेक्ट दोनों की जाँच करते हैं, क्योंकि कई बग उन मामलों में छिपते हैं जहाँ "OK लौटाया गया लेकिन गलत लिखा गया"।
उदाहरण के लिए, किसी ऑर्डर पर कूपन लागू करने वाले फ़ंक्शन के लिए:
अब इन्हें ठोस assertions में बदलें:
expect(result.total).toBeGreaterThanOrEqual(0)
expect(db.getOrder(orderId).discountCents).toBe(originalDiscountCents)
धुंदले asserts जैसे "अपेक्षित परिणाम लौटता है" से बचें। विशिष्ट नियम (non-negative), और विशिष्ट साइड-इफेक्ट (discount एक बार ही स्टोर हो) को असर्ट करें।
हर इनवेरिएंट के लिए टेस्ट में एक छोटा नोट जोड़ें कि कौन सा डेटा इसे तोड़ देगा। इससे टेस्ट बाद में happy-path चेक में बदलकर धुंधला नहीं होगा।
एक सरल पैटर्न जो समय के साथ मजबूती से चलता है:
हाई-सिग्नल टेस्ट अक्सर वे होते हैं जो पुष्टि करते हैं कि आपका कोड सुरक्षित ढंग से फेल करता है। अगर किसी मॉडल को केवल happy-path टेस्ट लिखने के लिए कहें तो आप यह नहीं जान पाएँगे कि फीचर इनपुट और निर्भरताओं के गड़बड़ होने पर कैसे व्यवहार करता है।
शुरू करें यह तय करके कि इस फीचर के लिए "सुरक्षित" का क्या अर्थ है। क्या यह एक टाइप्ड एरर लौटाता है? क्या यह एक डिफ़ॉल्ट पर फॉल बैक करता है? क्या यह एक बार retry करता है और फिर रुक जाता है? उस अपेक्षित व्यवहार को एक वाक्य में लिखें, फिर टेस्ट उसे साबित करें।
जब आप Claude Code से failure mode टेस्ट माँगते हैं, तो लक्ष्य सख्त रखें: सिस्टम के टूटने के तरीकों को कवर करें, और वह सटीक प्रतिक्रिया असर्ट करें जो आप चाहते हैं। एक उपयोगी लाइन है: "कमी टेस्ट लेकिन मजबूत assertions को प्राथमिकता दें बनाम बहुत सारे उथले टेस्ट।"
उत्पादक failure श्रेणियाँ जो बेहतर टेस्ट देती हैं:
उदाहरण: आपके पास एक endpoint है जो यूज़र बनाता है और वेलकम ईमेल भेजने के लिए ईमेल सर्विस को कॉल करता है। एक कम-मूल्य टेस्ट सिर्फ़ "returns 201" चेक कर सकता है। एक हाई-सिग्नल failure टेस्ट यह जाँचता है कि यदि ईमेल सर्विस टाइमआउट हो जाए तो आप या (a) फिर भी यूज़र बनाते हैं और 201 के साथ "email_pending" फ्लैग लौटाते हैं, या (b) स्पष्ट 503 लौटाते हैं और यूज़र नहीं बनाते। एक व्यवहार चुनें, फिर प्रतिक्रिया और साइड-इफेक्ट दोनों को असर्ट करें।
यह भी टेस्ट करें कि आप क्या लीक नहीं करते। यदि वैलिडेशन फेल करता है, तो सुनिश्चित करें कि कुछ भी डेटाबेस में नहीं लिखा गया। यदि किसी निर्भरता ने खराब payload लौटाया, तो सुनिश्चित करें कि आप अनहैंडल्ड exception न फेंकें या कच्चा स्टैक ट्रेस न लौटाएँ।
कम-मूल्य टेस्ट सेट आमतौर पर तब बनते हैं जब मॉडल को मात्रा के लिए पुरस्कृत किया जाता है। अगर आपका Claude Code टेस्ट जनरेशन प्रॉम्प्ट “20 यूनिट टेस्ट” माँगता है, तो आपको अक्सर छोटे-छोटे वैरिएशन मिलते हैं जो दिखने में गहन हैं पर कुछ नया नहीं पकड़ते।
सामान्य जाल:
उदाहरण: एक "create user" फ़ंक्शन की कल्पना करें। दस happy-path टेस्ट ईमेल स्ट्रिंग बदलकर भी महत्वपूर्ण चीज़ें मिस कर सकते हैं: duplicate emails को reject करना, खाली पासवर्ड संभालना, और गारंटी देना कि रिटर्न किए गए user IDs अद्वितीय और स्थिर हैं।
रिव्यू में मददगार गार्डरैलों:
कल्पना कीजिए एक फीचर: चेकआउट पर कूपन कोड लागू करना।
कॉन्ट्रैक्ट (छोटा और टेस्टेबल): एक कार्ट सबटोटल सेंट में और वैकल्पिक कूपन लेकर अंतिम टोटल सेंट में लौटाना। नियम: प्रतिशत कूपन निकटतम सेंट की ओर नीचे राउंड करते हैं, फिक्स्ड कूपन एक निश्चित राशि घटाते हैं, और टोटल कभी 0 से नीचे नहीं जाना चाहिए। एक कूपन अमान्य, समाप्त, या पहले से उपयोग किया जा चुका हो सकता है।
"applyCoupon() के लिए टेस्ट लिखो" न माँगें। बाउंडरी केस टेस्टिंग, failure mode टेस्ट, और कॉन्ट्रैक्ट से जुड़े इनवेरिएंट्स माँगें।
ऐसे इनपुट चुनें जो गणित या वेलिडेशन तोड़ते हैं: खाली कूपन स्ट्रिंग, subtotal = 0, न्यूनतम खर्च के ठीक नीचे और ऊपर, एक फिक्स्ड डिस्काउंट जो subtotal से बड़ा हो, और 33% जैसे प्रतिशत जो राउंडिंग पैदा करे।
मान लीजिए कूपन लुकअप विफल हो सकता है और स्टेट गलत हो सकता है: कूपन सेवा डाउन है, कूपन समाप्त है, या यह पहले ही उपयोग किया जा चुका है। टेस्ट को यह साबित करना चाहिए कि इसके बाद क्या होता है (कूपन रिजेक्ट किया गया साफ़ एरर के साथ, टोटल अपरिवर्तित)।
एक न्यूनतम, हाई-सिग्नल टेस्ट सेट (5 टेस्ट) और वे क्या पकड़ते हैं:
यदि ये पास हो गए, तो आपने सामान्य ब्रेकपॉइंट्स कवर कर लिए बिना सूट को duplicate happy-path टेस्ट से भर दिया।
मॉडल द्वारा जो कुछ भी जनरेट किया गया उसे स्वीकार करने से पहले एक तेज़ गुणवत्ता पास करें। लक्ष्य ऐसे टेस्ट हैं जो प्रत्येक किसी विशिष्ट, संभावित बग से आपको बचाते हैं।
इस चेकलिस्ट को गेट की तरह उपयोग करें:
एक त्वरित व्यावहारिक चाल: जनरेट किए गए टेस्ट का नाम बदलकर "should <behavior> when <edge condition>" और "should not <bad outcome> when <failure>" बनाएं। यदि आप उन्हें साफ़ तौर पर नहीं बदल पा रहे हैं, तो वे फोकस्ड नहीं हैं।
यदि आप Koder.ai के साथ ऐप बनाते हैं, तो यह चेकलिस्ट snapshots और rollback के साथ भी अच्छी तरह फिट बैठती है: टेस्ट जनरेट करें, उन्हें चलाएँ, और अगर नया सेट शोर जोड़ता है बिना कवरेज बढ़ाए, तो रोल बैक करें।
अपने प्रॉम्प्ट को एक एक-बार इस्तेमाल वाली चीज़ न मानें—इसे एक पुन: उपयोग योग्य हैर्नेस बनाएं। सीमाएँ, failure modes, और invariants को मजबूर करने वाला वह ब्लूप्रिंट प्रॉम्प्ट सहेजें और हर नए फ़ंक्शन, endpoint, या UI फ्लो के लिए दोहराएँ।
एक सरल आदत जो परिणामों को जल्दी सुधारेगी: हर टेस्ट के लिए एक वाक्य माँगें जो बताये कि यदि यह फेल हुआ तो यह किस बग को पकड़ता। अगर वह वाक्य सामान्य है, तो टेस्ट शायद शोर है।
अपना डोमेन इनवेरिएंट्स की एक जीवित सूची रखें। इसे अपने दिमाग में न रखें। जब भी आप वास्तविक बग पाते हैं, उसमें जोड़ें।
एक हल्का वर्कफ़्लो जिसे आप दोहरा सकते हैं:
यदि आप चैट के जरिए ऐप बनाते हैं, तो यह चक्र Koder.ai (koder.ai) के अंदर चलाएँ ताकि कॉन्ट्रैक्ट, प्लान, और जनरेट किए गए टेस्ट एक ही जगह पर रहें। जब कोई रीफ़ैक्टर अप्रत्याशित रूप से व्यवहार बदलता है, तो snapshots और rollback की मदद से तुलना करना और इटरेट करना आसान हो जाता है जब तक आपका हाई-सिग्नल सेट स्थिर न हो।
Default: aim for a small set that would catch a real bug.
A quick cap that works well is 6–10 tests per unit (function/module). If you need more, it usually means your unit is doing too much or your contract is unclear.
Happy-path tests mostly prove that your example still works. They tend to miss the stuff that breaks in production.
High-signal tests target:
Start with a tiny contract you can read in one breath:
Then generate tests from that contract, not from examples alone.
Test these first:
Pick one or two per input dimension so each test covers a unique risk.
A good failure-mode test proves two things:
If there’s a database write involved, always check what happened in storage after the failure.
Default approach: turn the invariant into an assertion on observable outcomes.
Examples:
expect(total).toBeGreaterThanOrEqual(0)Prefer checking both and , because many bugs hide in “returned OK but wrote the wrong thing.”
It’s worth keeping a happy-path test when it protects an invariant or a critical integration.
Good reasons to keep one:
Otherwise, trade it for boundary/failure tests that catch more classes of bugs.
Push for PHASE 1: plan only first.
Require the model to provide:
Only after you approve the plan should it generate code. This prevents “20 look-alike tests” output.
Default: mock only the boundary you don’t own (DB/network/clock), and keep everything else real.
To avoid over-mocking:
If a test breaks on refactor but behavior didn’t change, it’s often over-mocked or too implementation-coupled.
Use a simple deletion test:
Also scan for duplicates: