व्हाइट-बॉक्स टेस्टिंग और कोड कवरेज

White-box testing test object की internal structure से, ज़्यादातर उसके code से, tests derive करती और नापती है। सिर्फ़ "software को क्या करना चाहिए?" पूछने की बजाय आप पूछते हैं: "मेरे tests ने code के कौन-से हिस्से चलाए, और कौन-सा input बाकी हिस्सा चलाएगा?" उदाहरण: किसी if की true side तक आज तक कोई test नहीं पहुँचा; आप condition पढ़ते हैं, ऐसा input चुनते हैं जो उसे true बना दे, और expected result requirement से लेते हैं।

Code coverage इसके पीछे का माप है: एक tool record करता है कि tests के दौरान कौन-से statements, branches और functions चले, और बाकी को line-by-line चिह्नित करता है।

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

Black-box tests requirements से बनते हैं, इसलिए वे नहीं दिखा सकते कि वे implementation के कितने हिस्से तक पहुँचे। Code में अक्सर requirement से ज़्यादा होता है: error handling, special cases, पुराने rules, किसी developer का जोड़ा हुआ shortcut। अगर कोई test वह code नहीं चलाता, तो उसके defects बिना दिखे production तक पहुँच जाते हैं।

Tester के लिए coverage "क्या हमने काफ़ी testing की?" को ठोस सवालों में बदल देता है: किन branches तक कोई test नहीं पहुँचा, और क्या requirement बताती है कि वहाँ क्या होना चाहिए? जो testers unit tests नहीं लिखते, वे भी uncovered code से test basis की कमियाँ ढूँढते हैं।

White-box और black-box: tests के दो स्रोत

ISTQB glossary white-box testing को component या system की internal structure के analysis पर आधारित testing के रूप में परिभाषित करती है। यहाँ test basis implementation है: code, और साथ ही architecture, workflows और data flows। Coverage items इसी structure से आते हैं, जैसे किसी function के statements या branches, इसलिए ये tests design या code बनने के बाद ही design किए जा सकते हैं।

Equivalence partitioning और boundary value analysis जैसी black-box techniques specified behaviour से चलती हैं, इसलिए उन्हें code लिखे जाने से पहले design किया जा सकता है, और code कैसे भी लिखा जाए, वे valid रहती हैं। दोनों नज़रिए अलग-अलग चीज़ें देखते हैं:

  • Strength: white-box testing पूरे implementation को ध्यान में रखती है, इसलिए specification अस्पष्ट, पुरानी या अधूरी हो तब भी defects पकड़ लेती है।
  • Weakness: जो requirement कभी implement ही नहीं हुई, उसका कोई code नहीं होता जिसे cover किया जाए, इसलिए यह omission दिखाई नहीं देता। Requirement से बने black-box tests इसे पकड़ते हैं।
  • यह code चलाए बिना भी काम करती है: review या dry run में आप inputs को logic के अंदर trace करते हैं।
  • यह हर test level पर लागू होती है: component testing में branches, integration testing में components के बीच की calls, system testing में workflow के steps।
Black-box tests specification से आते हैं और नहीं दिखाते कि वे किस code तक पहुँचे; white-box tests code से आते हैं और कभी implement न हुई requirements नहीं देख पाते।

Black-box testingspecification-based

Software को क्या करना चाहिए, उससे बने tests

Test basis:
Requirements, user stories, specifications
Coverage items:
Partitions, boundary values
Tests कब design हो सकते हैं:
Code लिखे जाने से पहले
क्या पकड़ता है:
Requirement से अलग behaviour, और requirements जो कभी implement नहीं हुईं
क्या नहीं देख पाता:
Tests implementation के कितने हिस्से तक पहुँचे

White-box testingstructure-based

Software कैसे बना है, उससे बने tests

Test basis:
Code, architecture, workflows, data flows
Coverage items:
Statements, branches, conditions, paths
Tests कब design हो सकते हैं:
Design या code बनने के बाद ही
क्या पकड़ता है:
ऐसे code के defects जिसे requirements नहीं बतातीं, specification अस्पष्ट या अधूरी हो तब भी
क्या नहीं देख पाता:
कभी implement न हुई requirements: न code, न कुछ cover करने को

Teams दोनों इस्तेमाल करती हैं: पहले requirement-based tests, फिर उस code के लिए white-box tests जहाँ वे नहीं पहुँचे।

Code coverage कैसे नापा जाता है

Coverage tool code को instrument करता है (उसमें counters जोड़ता है, या runtime से execution record करवाता है), tests चलाता है और report लिखता है: हर file के लिए कितने प्रतिशत statements या lines, branches और functions चले, और source जिसमें हर line चिह्नित होती है: चली, नहीं चली, या आंशिक रूप से चली। Line coverage statement coverage के क़रीब है पर बिल्कुल वही नहीं: एक line में कई statements हो सकते हैं।

Criteria की ताक़त अलग-अलग होती है: statement coverage पूछता है कि क्या हर executable statement चला, branch coverage पूछता है कि क्या हर branch चली, जैसे हर if के true और false दोनों outcomes। अगले lessons statement, branch, condition और path coverage को एक-एक करके समझाते हैं। आम tools:

  • JavaScript और TypeScript: Istanbul (nyc command, और default रूप से Jest के --coverage के पीछे भी यही) या V8 engine का built-in coverage, जिसे Jest और Vitest भी इस्तेमाल कर सकते हैं।
  • Python: coverage.py, जो default रूप से statements नापता है और --branch के साथ branches भी।
  • Java: JaCoCo, जिसका Java agent application start होते समय जोड़ा जाता है, इसलिए वह server की manual testing के दौरान भी coverage record कर सकता है।
  • CI में ये tools report publish करते हैं और threshold से नीचे होने पर build fail कर सकते हैं, जैसे Jest का coverageThreshold या coverage.py का --fail-under।

Uncovered code से tests design करना

White-box design शायद ही कभी खाली पन्ने से शुरू होता है: teams requirements से tests बनाती हैं, उनका coverage नापती हैं, फिर gaps भरती हैं। हर uncovered item के लिए:

  • उसे guard करने वाला decision पढ़ें: वहाँ पहुँचने के लिए क्या true या false होना चाहिए?
  • Input तक पीछे जाएँ: parameters, data या state की कौन-सी values वह condition पूरी करती हैं? Comparison अपने आप में एक boundary है, इसलिए boundary value analysis मदद करती है।
  • Expected result test basis से लें, code से कभी नहीं: implementation से copy किया गया test वही confirm करता है जो code करता है, defects समेत।
  • अगर requirement इस behaviour के बारे में कुछ नहीं कहती, तो आपको test basis में एक gap मिला है: पहले पूछें।
  • Tests दोबारा चलाएँ: item अब covered होना चाहिए और नया test result को assertion से जाँचना चाहिए।
  • अगर कोई input उस code तक नहीं पहुँचता, तो coverage ज़बरदस्ती बढ़ाने की बजाय संभावित dead code report करें।
Coverage gap भरना: guard करने वाला decision पढ़ें, input तक पीछे जाएँ, expected result test basis से लें, और जब भी requirement चुप हो, रुककर पूछें।
  1. 1Requirement-based tests का coverage नापेंReport बताती है कि क्या कभी नहीं चला
  2. 2Guard करने वाला decision पढ़ेंवहाँ पहुँचने के लिए क्या true या false होना चाहिए?
  3. 3Input तक पीछे जाएँParameters, data या state की values; comparison एक boundary है

    शाखा: कोई input नहीं पहुँचता: संभावित dead code report करेंसावधानी

  4. 4Expected result test basis सेCode से कभी copy नहीं

    शाखा: Requirement चुप है: test लिखने से पहले पूछेंसावधानी

  5. 5दोबारा चलाएँ और जाँचेंItem covered, result assert हुआ

चरण 2 पर वापस अगला uncovered item

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

Worked example: वह line जिसका किसी requirement में ज़िक्र नहीं

Requirement: "Customer को order के हर पूरे 10.00 SAR पर 1 loyalty point मिलता है। Gold members को दोगुने points मिलते हैं।" नीचे developer का function, तीन black-box tests और CI की coverage report है। Math.floor नीचे की ओर round करता है, इसलिए 125.00 SAR का order 12 points कमाता है।

Line 10 कभी नहीं चली। यह app orders को 5 bonus points देती है, एक ऐसा rule जिसका requirement में ज़िक्र नहीं। +5 को expected result में copy करने की बजाय tester product owner से पूछता है, जो इसकी पुष्टि करता है: app से दिया गया हर order 5 bonus points कमाता है, 10.00 SAR से कम के orders भी।

  • T4 loyaltyPoints(125, false, true) 17 expect करता है और pass होता है। Statement और branch coverage अब 100% हैं।
  • लेकिन जिस rule की पुष्टि हुई, उसके लिए T5 loyaltyPoints(5, false, true) → 5 भी चाहिए। Code 0 लौटाता है, क्योंकि line 3 का early return bonus को छोड़ देता है: ऐसे path पर defect जिसकी full branch coverage को कभी ज़रूरत नहीं पड़ी।
  • Uncovered line ने एक missing requirement दिखाई, और उसके जवाब ने एक defect। Percentage ने इनमें से कुछ भी नहीं दिखाया।
 1  function loyaltyPoints(total: number, gold: boolean, app: boolean) {
 2    if (total < 10) {
 3      return 0;
 4    }
 5    let points = Math.floor(total / 10);
 6    if (gold) {
 7      points = points * 2;
 8    }
 9    if (app) {
10      points = points + 5;
11    }
12    return points;
13  }

Black-box tests                        Expected   Actual   Result
T1  loyaltyPoints(5, false, false)     0          0        passed
T2  loyaltyPoints(125, false, false)   12         12       passed
T3  loyaltyPoints(125, true, false)    24         24       passed

Coverage (CI run)   Statements    Branches    Not run
loyalty.ts          7/8  87.5%    5/6  83%    line 10: if (app) never true

आज़माएँ: डिसीज़न टेबल जनरेटर

Coverage आपको क्या नहीं बताता

Test coverage वाले lesson ने बुनियादी बातें बताई थीं: code चलाना उसे जाँचना नहीं है, और जो code कभी लिखा ही नहीं गया वह uncovered के रूप में नहीं दिख सकता। जब आप coverage से tests design करें, तो यह भी याद रखें:

  • Expected results सही हैं या नहीं: test ग़लत value assert कर सकता है, जैसे code से copy की गई value, और फिर भी गिना जाता है।
  • Combinations: full branch coverage हर decision को दोनों तरफ़ ले जाता है, हर combination को नहीं। T1 से T4 100% तक पहुँचे और "app से दिया गया छोटा order" छूट गया।
  • Data पर निर्भर defects: जो statement harmless data के साथ चला, वह दूसरी values पर fail हो सकता है, जैसे शून्य से भाग।
  • Report के बाहर का code: उदाहरण के लिए Jest, collectCoverageFrom set न हो तो सिर्फ़ वही files गिनता है जो tests ने load कीं; libraries, database procedures, configuration और दूसरी services आम तौर पर नापी नहीं जातीं।
  • Non-functional quality: performance, security, usability और accessibility के लिए अपने tests चाहिए।

आज़माएँ: Requirements → टेस्ट केस

आम गलतियाँ

  • Expected result code under test से copy करना, जिससे test defect को पकड़ने की बजाय confirm कर देता है।
  • Coverage target को ही लक्ष्य मान लेना और ऐसे tests जोड़ना जो कुछ assert नहीं करते।
  • 100% statement coverage को "पूरी तरह tested" समझना, जबकि branches, combinations और data पर निर्भर cases अभी untested हैं।
  • Behaviour क्या होना चाहिए यह पूछे बिना किसी undocumented branch को चुपचाप cover कर देना।
  • सिर्फ़ white-box testing पर निर्भर रहना: missing requirements का कोई code नहीं होता जिसे cover किया जाए।
  • सिर्फ़ total पढ़ना और यह न देखना कि कौन-सी files और lines uncovered हैं, या report में कौन-सी files शामिल ही हैं।

QA का नज़रिया

ज़्यादातर teams में unit tests developers के होते हैं, और coverage tool हर change पर CI pipeline में चलता है। Tester report को risks ध्यान में रखकर पढ़ता है: changed और critical code की uncovered lines माँगता है, देखता है कि क्या उनमें ऐसा behaviour छिपा है जो requirements में नहीं है, और review करता है कि cover करने वाले tests सही results assert करते हैं या नहीं। किसी developer के साथ uncovered branches पर साथ में चलना शुरुआत का सबसे तेज़ तरीका है।

Coverage report करते समय criterion और suite का नाम लें ("unit tests, changed files की branch coverage: 92%, refund handling में दो uncovered branches, सवाल उठाए गए"), और हर उस सवाल को track करें जो किसी uncovered branch ने उठाया: उसका जवाब या तो missing test है, या missing requirement, या dead code।

अभ्यास

Account lockout function में coverage gap भरें

आपका काम

Requirement: "लगातार 5 ग़लत passwords के बाद account lock हो जाता है। Locked account सही password से भी sign in नहीं कर सकता। सही password ग़लत attempts की गिनती reset कर देता है।" नीचे login function, उसके तीन unit tests और coverage report है।

वह rule खोजें जिसका कोई test नहीं है, ऐसा test design करें जो uncovered line चलाए और जिसका expected result requirement से लिया गया हो, और तय करें कि वह pass होगा या नहीं। फिर लिखें कि 100% coverage भी आपको क्या नहीं बताएगा।

सामग्री
 1  function login(user: User, password: string): string {
 2    if (user.locked) {
 3      return 'LOCKED';
 4    }
 5    if (checkPassword(user, password)) {
 6      user.failedAttempts = 0;
 7      return 'OK';
 8    }
 9    user.failedAttempts = user.failedAttempts + 1;
10    if (user.failedAttempts > 5) {
11      user.locked = true;
12    }
13    return 'WRONG_PASSWORD';
14  }

Unit tests                                     Expected          Result
T1  correct password, failedAttempts = 2       'OK'              passed
T2  wrong password, failedAttempts = 0         'WRONG_PASSWORD'  passed
T3  correct password, user.locked = true       'LOCKED'          passed

Coverage     Statements    Branches    Not run
login.ts     8/9  89%      5/6  83%    line 11
चरण
  1. Requirement के हर वाक्य को T1, T2 और T3 से जोड़ें।
  2. Line 11 को guard करने वाली condition और उसे true बनाने वाली state खोजें।
  3. नया test और उसका boundary पड़ोसी लिखें, फिर उन्हें code में trace करें: pass या fail?
  4. लिखें कि 100% statement और branch coverage पर भी क्या अज्ञात रहता है।
मॉडल उत्तर दिखाएँ
मॉडल उत्तर

T3 दूसरे वाक्य को cover करता है: "locked account sign in नहीं कर सकता"। T1 एक सफल sign-in के साथ line 6 का reset (तीसरा वाक्य) भी चलाता है, और T2 एक ग़लत password गिनता है। पहले वाक्य, यानी 5 ग़लत passwords के बाद lock, को कुछ भी cover नहीं करता: यह rule lines 10 और 11 है, और line 11 कभी नहीं चली।

Line 11 के लिए line 9 के increment के बाद user.failedAttempts > 5 true होना चाहिए। 5वें ग़लत password को account lock करना चाहिए, इसलिए test है: failedAttempts = 4, ग़लत password, expected user.locked === true। इस attempt के लिए 'WRONG_PASSWORD' सिर्फ़ इसलिए assert न करें कि code वही लौटाता है: requirement नहीं बताती कि lock करने वाला attempt क्या लौटाए, इसलिए पूछें। Boundary पड़ोसी: failedAttempts = 3, ग़लत password, account unlocked रहता है।

Boundary पड़ोसी pass होता है, लेकिन नया test fail होता है: 4 + 1 = 5 और 5 > 5 false है, इसलिए account 6वें ग़लत password के बाद ही lock होता है। इस off-by-one defect को input, expected और actual result और line 10 के साथ report करें; fix >= 5 है।

Code से copy किया गया test (failedAttempts = 5, expected locked) 100% coverage तक पहुँच जाता और pass होता, यानी defect को confirm करता।

100% पर भी अज्ञात: क्या T1 assert करता है कि failedAttempts 0 पर reset हुआ, क्या गिनती sessions, devices और एक साथ आने वाले requests में सही रहती है, क्या checkPassword सही है, और locked account unlock कैसे होता है, जिसे न requirement बताती है न code: यह product owner के लिए सवाल है।

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

क्या white-box testing के लिए programmer होना ज़रूरी है?

आपको code इतना पढ़ना आना चाहिए कि उसके decisions follow कर सकें: conditions, loops और early return। Production code लिखना ज़रूरी नहीं। कई testers coverage reports पढ़ने और किसी developer के साथ unit tests review करने से शुरुआत करते हैं, और वहीं से आगे बढ़ते हैं।

क्या white-box testing और unit testing एक ही हैं?

नहीं। Unit (component) testing एक test level है; white-box testing tests derive करने और नापने का तरीका है। इसकी techniques सबसे ज़्यादा component level पर इस्तेमाल होती हैं, पर integration और system testing पर भी लागू होती हैं, और unit tests ख़ुद अक्सर component की specification से black-box तरीके से design होते हैं।

क्या line coverage और statement coverage एक ही हैं?

क़रीब, पर बिल्कुल एक नहीं। एक line में कई statements या a && b ? x : y (a && b true हो तो x, नहीं तो y) जैसा पूरा decision हो सकता है, इसलिए line covered गिनी जा सकती है जबकि उसका कुछ हिस्सा कभी नहीं चला। Branch के आँकड़े और आंशिक रूप से covered lines के लिए tool के markers वह दिखाते हैं जो line coverage छिपा लेता है।

क्या manual tests का coverage नापा जा सकता है?

हाँ, अगर application instrumented build के रूप में या coverage agent के साथ चले, जैसे Java server के लिए JaCoCo का agent। तब report दिखाती है कि manual या exploratory session किस code तक पहुँचा। इससे अनछुए हिस्से मिलते हैं, और सीमा वही रहती है: यह नहीं बता सकता कि आपने ग़लत result पर ध्यान दिया या नहीं।

संदर्भ

  • ISTQB Certified Tester Foundation Level (CTFL) v4.0ISTQB · 2023सॉफ़्टवेयर टेस्टिंग का फ़ाउंडेशन पाठ्यक्रम: टेस्ट स्तर और प्रकार, टेस्ट डिज़ाइन तकनीकें, टेस्ट प्रबंधन और टूल।
  • ISTQB GlossaryISTQBटेस्टिंग शब्दों की मानक परिभाषाएँ — टेस्ट केस, रिग्रेशन टेस्टिंग, सेवेरिटी, प्रायोरिटी, बाउंड्री वैल्यू एनालिसिस आदि।
  • ISO/IEC/IEEE 29119-1:2022 — Software and systems engineering — Software testing — Part 1: General conceptsISO/IEC/IEEE · 2022सॉफ़्टवेयर टेस्टिंग का अंतरराष्ट्रीय मानक: अवधारणाएँ, प्रक्रियाएँ, दस्तावेज़ीकरण और तकनीकें।
  • 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 से अभ्यास करें

conditions और actions से decision table बनाएँ, सभी rule संयोजन जनरेट करें और असंभव संयोजनों को समेट दें।

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

क्रमांकित आवश्यकताओं की सूची को requirement-to-test मैट्रिक्स के साथ ट्रेस करने योग्य टेस्ट केस में बदलें।

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

और गहराई से

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

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