ब्रांच और डिसीज़न कवरेज

Decision coverage नापता है कि आपके tests ने code के decisions के कितने outcomes चलाए: हर if की दोनों sides, switch का हर case, और loop के दोनों outcomes, "फिर से चलो" और "बाहर निकलो"। Branch coverage, जिसे ISTQB Foundation syllabus v4.0 इसकी जगह सिखाता है, control flow की branches नापता है और आम code में वही tests माँगता है।

उदाहरण: if (member) { discountPercent = 20; } के दो outcomes हैं। Member वाला एक test हर statement चला देता है, लेकिन non-member वाला outcome, जिसमें if के अंदर कुछ नहीं चलता, कभी try नहीं होता। दोनों criteria उसके लिए दूसरा test माँगते हैं।

यह क्यों ज़रूरी है

Statement coverage, यानी पिछले lesson का विषय, वह outcome नहीं देख पाता जो code को skip करता है: if के अंदर के statements एक बार चल गए, तो वह कभी नहीं पूछता कि उन्हें skip करने पर क्या होता है। कई defects ठीक वहीं रहते हैं: कोई variable जो if न लिए जाने पर गलत default रख लेता है, छूटा हुआ else, या ऐसा input जिसे if (qty > 0) जैसा guard छाँट देना चाहिए पर जिसे कोई test भेजता ही नहीं। Branch coverage इनमें से हर outcome से एक test गुज़रवाता है।

Tester के लिए हर uncovered outcome एक ठोस सवाल है: non-member, खाली cart या अनजान status के लिए क्या होना चाहिए? जवाब एक test बनता है जिसका expected result requirement से आता है, या कोई defect या छूटी हुई requirement सामने लाता है।

Decisions और उनके outcomes

Decision code का वह point है जहाँ code तय करता है कि आगे कौन-सा statement चलेगा; हर संभव चुनाव एक decision outcome है। Decision coverage चलाए गए decision outcomes की संख्या को कुल decision outcomes से भाग देकर, प्रतिशत में निकाला जाता है। पहले हर decision ढूँढें; कुछ आसानी से नज़र से छूट जाते हैं:

  • else के साथ या बिना if: दो outcomes। else न हो तो false outcome block को skip करता है और उसका अपना कोई statement नहीं होता।
  • Conditional operator a ? b : c: एक ही statement के अंदर दो outcomes, इसलिए statement और line coverage उसे covered गिन लेते हैं, भले एक side कभी न चली हो।
  • Loops (for, while): body में जाना या बाहर निकलना। Body को एक बार चलाने वाला test दोनों outcomes ले लेता है, इसलिए zero iterations कभी required नहीं होते।
  • switch: हर case के लिए एक outcome, और default outcome भी, चाहे default clause लिखा न हो।
  • if (a && b) दो outcomes वाला एक decision है; उसके अंदर की conditions अगले lesson का विषय हैं।
  • घना या minified code decisions छिपा देता है: पहले उसे format करें ताकि हर statement, यानी हर if और loop भी, अपनी line पर हो, फिर expressions के अंदर ?: ढूँढें।

आज़माएँ: JavaScript फ़ॉर्मेटर

Branch coverage और decision coverage से उसका रिश्ता

ISTQB Foundation syllabus v4.0 branch testing सिखाता है। Branch control flow graph के दो nodes के बीच control का transfer है; यह graph दिखाता है कि statements किन क्रमों में चल सकते हैं, और इसके nodes statements या statements के blocks होते हैं। Straight-line code में branch unconditional होती है, और decision outcome होने पर conditional। Branch coverage चलाई गई branches की संख्या को कुल branches से भाग देकर, प्रतिशत में निकाला जाता है।

दोनों criteria अलग चीज़ें गिनते हैं, इसलिए 100% से नीचे उनके प्रतिशत अलग हो सकते हैं, लेकिन आम code में वही tests दोनों में 100% तक पहुँचते हैं: जब हर decision outcome चल चुका, तो उसके बाद वाला हर block भी चल चुका। कई teams दोनों नामों को पर्यायवाची की तरह इस्तेमाल करती हैं; syllabus v3.1 decision coverage सिखाता था और v4.0 ने उसकी जगह branch coverage रखा।

  • Coverage tools "branches" report करते हैं और ज़्यादातर उनका मतलब decision outcomes होता है; उदाहरण के लिए coverage.py इन्हें --branch के साथ चलाने पर नापता है।
  • Tools अलग-अलग चीज़ें गिनते हैं: कुछ && और || के हर operand को, या compiled code के हर jump को भी गिनते हैं। इसलिए प्रतिशत सिर्फ़ एक ही tool के बीच compare करें।

100% branch coverage में 100% statement coverage क्यों शामिल है

हर executable statement कम से कम एक branch के सिरे पर होता है: control उस तक function के entry से, उससे पहले वाले statement से, या किसी decision outcome से होकर पहुँचता है। अगर आपके tests हर branch से गुज़र चुके, तो control हर statement तक पहुँच चुका, यानी हर statement चल चुका। इसीलिए syllabus कहता है कि branch coverage statement coverage को subsume करता है: tests का जो भी set 100% branch coverage पाता है, वह 100% statement coverage भी पाता है।

उल्टा सच नहीं है। बिना else वाले if के false outcome में कोई statement नहीं होता, इसलिए tests हर statement चला सकते हैं और उस outcome से कभी न गुज़रें।

  • Subsumption सिर्फ़ 100% की बात है। उससे नीचे कोई भी प्रतिशत बड़ा हो सकता है: बिना else वाले if की सिर्फ़ true side लेने वाला test 100% statements और 50% decision outcomes पा सकता है, जबकि जो test ऐसे if/else की सिर्फ़ छोटी side लेता है जिसकी दूसरी side लंबी है, वह 50% decision outcomes और statements का बहुत छोटा हिस्सा पाता है।
  • मज़बूत का मतलब काफ़ी नहीं: 100% branch coverage outcomes के combinations, शून्य से भाग जैसे data-dependent defects, या कभी न लिखे गए code के बारे में कुछ नहीं बताता।

Worked example: ऐसा discount जो किसी ने माँगा ही नहीं

Requirement: "Members को price पर 20% छूट मिलती है। Non-members पूरी price देते हैं। Discount के बाद 200.00 SAR से कम के orders 25.00 SAR shipping देते हैं; 200.00 SAR या उससे ज़्यादा के orders की shipping free है।" नीचे के code में 8 executable statements और दो decisions हैं, lines 3 और 8 पर: यानी 4 decision outcomes।

  • T1 checkout(100, true) 80 + 25 = 105 expect करता है और pass होता है। यह सभी 8 statements चलाता है (100% statement coverage), लेकिन हर decision का सिर्फ़ true outcome लेता है: 4 में से 2, यानी 50% decision coverage।
  • छूटे हुए: line 3 false (non-member) और line 8 false (200.00 या ज़्यादा का total)। एक test दोनों लेता है: T2 checkout(300, false), जो 300 expect करता है।
  • T2 fail होता है: code 270 लौटाता है, क्योंकि line 2 हर customer को 10% छूट से शुरू करती है, ऐसा default जिसका requirement में ज़िक्र तक नहीं। Non-members को ऐसी छूट मिलती है जिसके वे हकदार नहीं, और 200.00 से थोड़ा ऊपर के उनके कुछ orders इसी वजह से free shipping खो देते हैं।
  • T1 और T2 के साथ decision coverage 100% है। जो team T1 के बाद 100% statement coverage पर रुक जाती, वह यह defect release कर देती।
  • Criterion के लिए हर side की कोई भी value काफ़ी है; boundary value analysis < को जाँचने के लिए 199.99 और 200.00 के totals जोड़ता है। Fix के बाद इन सबको confirmation testing के रूप में दोबारा चलाएँ।
 1  function checkout(price: number, member: boolean): number {
 2    let discountPercent = 10;
 3    if (member) {
 4      discountPercent = 20;
 5    }
 6    const total = price - (price * discountPercent) / 100;
 7    let shipping = 0;
 8    if (total < 200) {
 9      shipping = 25;
10    }
11    return total + shipping;
12  }

Test                        Expected  Actual  Result  Outcomes taken
T1  checkout(100, true)     105       105     passed  line 3 true,  line 8 true
T2  checkout(300, false)    300       270     failed  line 3 false, line 8 false

Coverage    Statements    Decision outcomes   Not taken
T1          8/8  100%     2/4   50%           line 3 false, line 8 false
T1 + T2     8/8  100%     4/4  100%           none
दो tests checkout function के चारों decision outcomes cover करते हैं: T1 दोनों true outcomes लेता है और अकेले हर statement चलाता है, T2 दोनों false outcomes लेता है और 10% default discount पर fail होता है।
प्रश्न 1: Line 3: if (member)Decision 1: दो outcomes
  • उत्तर: true: line 4 discount 20% करती है

    प्रश्न 2: Line 8: if (total < 200)Decision 2: T1 में total 80
    • उत्तर: true: line 9 shipping में 25 जोड़ती है

      परिणाम: T1 checkout(100, true)Expected 105, actual 105; अकेले सभी 8 statements चलाता हैपास
    • उत्तर: false: line 9 skip, shipping free

      परिणाम: Discount के बाद 200.00 या ज़्यादा का member orderसिर्फ़ एक path: इसके हर outcome को T1 और T2 पहले ही ले चुके100% के लिए ज़रूरी नहीं
  • उत्तर: false: line 4 skip, 10% बना रहता है

    प्रश्न 3: Line 8: if (total < 200)Decision 2: T2 में total 270
    • उत्तर: true: line 9 shipping में 25 जोड़ती है

      परिणाम: 200.00 से कम का non-member orderसिर्फ़ एक path: इसके हर outcome को T1 और T2 पहले ही ले चुके100% के लिए ज़रूरी नहीं
    • उत्तर: false: line 9 skip, shipping free

      परिणाम: T2 checkout(300, false)Expected 300, actual 270: 10% वाला defaultफ़ेल

Dashed रास्ते उन्हीं outcomes को जोड़ते हैं जो T1 और T2 ले चुके: ये अतिरिक्त paths हैं, जिन्हें branch coverage नहीं माँगता।

आज़माएँ: बाउंड्री वैल्यू जनरेटर आज़माएँ: टेस्ट केस जनरेटर

आम गलतियाँ

  • सिर्फ़ उन्हीं outcomes को गिनना जिनका code है: बिना else वाले if की false side भी एक outcome है, और statement coverage उसी को छोड़ता है।
  • 100% statement coverage को 100% branch coverage समझना, या ऐसे tools के branch प्रतिशत compare करना जो branches अलग तरीके से गिनते हैं।
  • किसी outcome तक पहुँचकर expected result requirement की बजाय code से copy कर लेना।
  • यह मान लेना कि 100% branch coverage का मतलब outcomes का हर combination, हर loop count या हर data value टेस्ट हो गई; इसका मतलब सिर्फ़ यह है कि हर outcome कम से कम एक बार चला।
  • switch के default जैसे implicit outcomes भूल जाना, या यह मान लेना कि खाली list वाला case covered है क्योंकि loop का exit चल चुका।

QA का नज़रिया

Branch coverage आम तौर पर CI में developers के unit tests पर नापा जाता है। Project के total की बजाय बदले हुए code का branch figure माँगें, और uncovered outcomes की list भी: हर outcome behaviour के बारे में एक सवाल है। Developer के साथ नए unit tests review करते समय जाँचें कि हर test requirement वाला result assert करता है, न कि सिर्फ़ line तक पहुँचता है।

Reports में criterion और tool का नाम लें: "pricing code, branch coverage (coverage.py --branch): 20 में से 18 outcomes; छूटे हुए दो non-member और खाली cart वाले cases हैं, उनके tests जोड़ दिए गए"। प्रतिशत test design की निचली सीमा है, quality का सबूत नहीं।

अभ्यास

Cart total के लिए 100% branch coverage पाएँ

आपका काम

Requirement: "Cart का total उसकी हर line के price × quantity का जोड़ है। Quantity 0 वाली lines ignore होती हैं। Coupon WELCOME20 total से 20.00 SAR घटाता है; coupon के बिना total कम नहीं होता।" नीचे cartTotal function और उसका इकलौता unit test है, जो pass होता है।

Coverage tools की तरह decision outcomes गिनकर मौजूदा test का coverage नापें, फिर कम से कम extra tests से 100% decision coverage तक पहुँचें, और उसके साथ 100% branch coverage तक भी।

सामग्री
 1  function cartTotal(items, coupon) {
 2    let total = 0;
 3    for (const item of items) {
 4      if (item.qty > 0) {
 5        total = total + item.price * item.qty;
 6      }
 7    }
 8    let discount;
 9    if (coupon === "WELCOME20") {
10      discount = 20;
11    }
12    return total - discount;
13  }

Unit test                                              Expected   Result
T1  cartTotal([{ price: 50, qty: 3 }], "WELCOME20")    130        passed
चरण
  1. Loop समेत हर decision ढूँढें और outcomes गिनें।
  2. T1 को trace करें और उसका statement और decision coverage निकालें।
  3. छूटे हुए outcomes के लिए tests design करें, expected results requirement से लेकर।
  4. नए tests trace करें: क्या वे pass होते हैं? 100% अब भी क्या नहीं माँगता?
मॉडल उत्तर दिखाएँ
मॉडल उत्तर

Decisions: line 3 का loop (अंदर जाना या बाहर निकलना), line 4 का if और line 9 का if: 6 outcomes।

T1 हर statement चलाता है: 100% statement coverage। Cart में एक line होने से loop एक बार अंदर जाता है और फिर बाहर निकलता है, इसलिए T1 loop के दोनों outcomes लेता है, साथ ही line 4 true और line 9 true: 6 में से 4, यानी 67% decision coverage।

छूटे हुए: line 4 false (quantity 0 वाली line) और line 9 false (कोई coupon नहीं)। एक test दोनों लेता है: T2 cartTotal([{ price: 50, qty: 3 }, { price: 30, qty: 0 }], "") 150 expect करता है।

T2 fail होता है: coupon के बिना discount को कभी value नहीं मिलती, और line 12 150 - undefined लौटाती है, जो NaN है। Input, expected 150, actual NaN और line 8 report करें; fix है let discount = 0;। Statement coverage यह test कभी नहीं माँगता, क्योंकि line 9 के false outcome में कोई statement नहीं है।

अब भी required नहीं: खाली cart (T1 loop से बाहर निकल चुका) और total से बड़ा coupon। खाली cart boundary value analysis से जोड़ें, और बड़े coupon के बारे में product owner से पूछें।

अक्सर पूछे जाने वाले सवाल

क्या branch coverage और decision coverage एक ही हैं?

लगभग। Decision coverage decision outcomes गिनता है; branch coverage, जैसा ISTQB v4.0 परिभाषित करता है, control flow graph की हर branch गिनता है, conditional और unconditional दोनों। आम code में वही tests दोनों में 100% तक पहुँचते हैं, और कई teams और tools दोनों नाम एक-दूसरे की जगह इस्तेमाल करते हैं। बताएँ कि आपका मतलब कौन-सा है और किस tool ने नापा।

क्या 100% branch coverage का मतलब है कि हर path टेस्ट हो गया?

नहीं। दो independent if statements वाले function के चार paths होते हैं, और दो tests सभी चार outcomes cover कर सकते हैं जबकि वे सिर्फ़ दो paths चलाते हैं, जैसा worked example में हुआ। Outcomes और conditions के combinations को cover करना अगले lesson, condition और path coverage, का विषय है।

Team को कितने प्रतिशत branch coverage का लक्ष्य रखना चाहिए?

कोई universal number नहीं है। Teams risk के हिसाब से निचली सीमा तय करती हैं: pricing, security या safety logic के लिए ऊँची, साधारण glue code के लिए कम, और अक्सर सिर्फ़ बदले हुए code पर। Target बढ़ाने से ज़्यादा फ़ायदा यह review करने में है कि कौन-से outcomes uncovered हैं और क्यों। प्रतिशत कभी ऐसे tests से न बढ़ाएँ जो कुछ assert नहीं करते।

संदर्भ

  • ISTQB Certified Tester Foundation Level (CTFL) v4.0ISTQB · 2023सॉफ़्टवेयर टेस्टिंग का फ़ाउंडेशन पाठ्यक्रम: टेस्ट स्तर और प्रकार, टेस्ट डिज़ाइन तकनीकें, टेस्ट प्रबंधन और टूल।
  • ISTQB GlossaryISTQBटेस्टिंग शब्दों की मानक परिभाषाएँ — टेस्ट केस, रिग्रेशन टेस्टिंग, सेवेरिटी, प्रायोरिटी, बाउंड्री वैल्यू एनालिसिस आदि।
  • NIST SP 500-235: Structured Testing: A Testing Methodology Using the Cyclomatic Complexity MetricNIST · 1996Structured (basis path) testing: McCabe की cyclomatic complexity से टेस्ट करने के लिए स्वतंत्र paths का basis set निकालना — statement और branch coverage से अधिक गहन।

Mutqan tools से अभ्यास करें

फ़ीचर के विवरण और फ़ील्ड सूची से संरचित टेस्ट केस (id, शीर्षक, पूर्व-शर्तें, स्टेप, अपेक्षित परिणाम, प्राथमिकता) जनरेट करें।

टेस्ट डिज़ाइन

JavaScript और TypeScript कोड को एकसमान इंडेंटेशन और स्पेसिंग के साथ सुंदर बनाएँ।

फ़ॉर्मेटिंग

और गहराई से

इस topic में आपकी प्रगति

QA रोडमैप का हिस्सा