عدد «۱۰۰٪» روی داشبورد Checkout سبز است؛ ۶۲۴ واحد از ۶۲۴ واحد پوشش داده شده‌اند و ۳۸۰ تست هم Pass شده‌اند. مدیر می‌پرسد: «پس می‌توانیم منتشر کنیم؟» پاسخ حرفه‌ای نه «بله» است و نه «پوشش تست بی‌فایده است». ابتدا باید پرسید: ۶۲۴ چه چیزهایی‌اند، از کدام Build، با تعریف کدام ابزار، چه مواردی از مخرج حذف شده‌اند و این عدد دقیقاً اجازهٔ چه ادعایی را می‌دهد؟

این راهنما «پوشش تست ۱۰۰٪» را از یک عدد نمایشی به یک Coverage Claim قابل‌ممیزی تبدیل می‌کند. خروجی عملی آن قرارداد مخرج یا Denominator، رجیستری Universe، Evidence Manifest، قواعد Exclusion و Unknown و پروتکل Correction است. اگر دنبال آموزش تکنیک‌های Statement و Branch هستید، ابتدا راهنمای تست جعبه سفید و پوشش کد را بخوانید؛ این مقاله مالکِ اعتبار خودِ ادعای درصد است.

خلاصه اجرایی: چه زمانی Coverage Claim معتبر است؟

  • پوشش، نسبت «واحدهای واجد شرایطِ مشاهده‌شده» به «کل واحدهای واجد شرایط در یک Universe نسخه‌دار» است؛ نه امتیاز کیفیت محصول.
  • عبارت ۱۰۰٪ بدون نوع Coverage، واحد شمارش، Subject، Build، ابزار، تنظیمات، بازهٔ زمانی و سیاست Exclusion معنای قابل‌بازتولید ندارد.
  • اجراشدن با Assert شدن، درست‌بودن Oracle، پوشش سناریو، نبود نقص، رضایت کاربر یا آمادگی Release یکی نیست.
  • EXCLUDED، NOT_APPLICABLE، UNKNOWN، STALE و INVALID پنج حالت متفاوت‌اند؛ مخلوط‌کردن آن‌ها مخرج را زیبا اما تصمیم را ضعیف می‌کند.
  • هدف ۸۰٪، ۹۰٪ یا ۱۰۰٪ استاندارد عمومی نیست. Threshold باید به تصمیم، ریسک، هزینه و Guardrail متصل باشد.
  • اگر Universe یا تعریف ابزار عوض شده باشد، مقایسهٔ دو درصد فقط پس از تصمیم Comparability معتبر است.
  • یک Claim خوب علاوه بر عدد، محدودیت ادعا، Evidence، Unknown، Owner، Review Due و مسیر Correction دارد.

هدف جست‌وجو و مرز این راهنما

نیت اصلی کاربر اطلاعاتی و اجرایی است: «آیا پوشش تست ۱۰۰ درصد خوب است؟»، «درصد مناسب Code Coverage چقدر است؟»، «چرا پوشش ۱۰۰٪ تضمین کیفیت نیست؟» و «چطور عدد Coverage را درست محاسبه کنیم؟». پاسخ کوتاه این است که هیچ درصدی مستقل از قرارداد اندازه‌گیری تفسیر نمی‌شود.

برای طراحی یک سیستم چندبعدی شامل Requirement، Risk، Behavior، Configuration و Production Evidence، صفحهٔ پوشش تست نرم‌افزار از Code Coverage تا Risk Evidence مرجع اصلی است. برای تعریف KPI و جلوگیری از بازی متریک نیز راهنمای متریک‌های تست نرم‌افزار را ببینید. این صفحه روی یک سؤال محدود اما سخت تمرکز دارد: آیا یک Coverage Claim مشخص، با مخرج و شواهد خودش معتبر و قابل‌تصحیح است؟

پوشش تست چیست؟ تعریف عملی، نه تعریف تزئینی

تعریف عملی زیر برای پوشش ساختاری کد، پوشش نیازمندی، ریسک، حالت، پیکربندی یا هر Universe شمارش‌پذیر قابل‌استفاده است:

Coverage Claim = {
  purpose, decision, subject, build,
  coverageType, unit, coveredSemantics,
  universeVersion, eligiblePredicate,
  numerator, denominator, formula,
  toolAndConfig, evidenceManifest,
  exclusions, unknowns, freshness,
  result, claimLimit, owner, correction
}

Coverage % = 100 × Covered eligible unique units
                 ─────────────────────────────
                   All eligible unique units

کلمات «eligible»، «unique» و «covered» عمدی‌اند. اگر واحدی دوبار شمرده شود، اگر مورد خارج از Scope وارد صورت شود، اگر Unknown از مخرج ناپدید شود یا اگر صرفاً یک خط اجراشده را «رفتار تأییدشده» بنامیم، درصد از نظر حسابی ممکن است درست و از نظر تصمیم‌گیری نامعتبر باشد.

Coverage Claim با Quality Claim چه تفاوتی دارد؟

ادعاآنچه می‌تواند بگویدآنچه به‌تنهایی نمی‌تواند بگوید
Statement/Line Coverageواحد تعریف‌شده توسط Collector مشاهده شده استخروجی درست، سناریو کامل یا نقصی وجود ندارد
Branch Coverageمقصدهای تعریف‌شدهٔ Branch طی Run دیده شده‌اندهمهٔ شرط‌ها، Pathها یا رفتارهای کسب‌وکار بررسی شده‌اند
Requirement Mappingرابطه‌ای میان Requirement و Artifact ثبت شده استتست اجرا شده، Pass معتبر یا Requirement درست است
Risk Evidence Coverageبرای ریسک‌های واجد شرایط Evidence طبق قرارداد وجود داردریسک حذف شده یا Release ایمن است
Mutation Scoreتست‌ها نسبت به مجموعهٔ انتخاب‌شده‌ای از Mutantها حساس بوده‌اندOracle کامل، تست کافی یا محصول بدون نقص است

برای سنجش جداگانهٔ قدرت آشکارسازی خطا، راهنمای عملی Mutation Testing و برای صحت Expected Result، قرارداد Test Oracle و Verdict را بخوانید. Reachability، Reveal Sensitivity و Decision Adequacy سه سؤال متفاوت‌اند.

چرا «۱۰۰٪» بدون مخرج یک جمله ناقص است؟

فرض کنید تیم الف ۹۳۰ خط از ۱۰۰۰ خط و تیم ب ۶۰۰ خط از ۶۰۰ خط را پوشش داده است. تیم ب ۱۰۰٪ و تیم الف ۹۳٪ دارد؛ اما تیم ب ۴۰۰ خط Generated، Adapter قدیمی و مسیر Feature Flag را بدون رجیستری از تحلیل خارج کرده است. سؤال درست این نیست که کدام عدد بزرگ‌تر است؛ سؤال این است که Universe هر Claim چگونه ساخته شده و آیا Exclusionها مجاز، نسخه‌دار و قابل‌بازبینی‌اند.

عدد یکسانتعبیرهای ناسازگار محتمل
۱۰۰٪ Lineهر خط حداقل یک Instruction دیده؛ یا هر Statement دیده؛ یا فقط فایل‌های Includeشده دیده شده‌اند
۱۰۰٪ BranchBranch ابزار الف؛ Arc ابزار ب؛ بدون Exception Branch؛ با/بدون Generated code
۱۰۰٪ Requirementفقط Link وجود دارد؛ یا طراحی Ready است؛ یا Evidence تازه و معتبر وجود دارد
۱۰۰٪ Passتمام تست‌های اجراشده Pass؛ تست‌های Skip/Blocked حذف؛ یا فقط Retried final attempt گزارش شده

در مستند رسمی Coverage Counterهای JaCoCo، کوچک‌ترین واحد Instruction در bytecode است؛ Line به debug information وابسته است، کد synthetic کامپایلر می‌تواند نتیجهٔ غیرمنتظره بسازد و exception handling در تعریف Branch این ابزار شمرده نمی‌شود. در مقابل، مستند Branch Coverage در coverage.py از جفت مبدأ–مقصد خط و execution opportunity صحبت می‌کند. نتیجه: حتی دو گزارش با برچسب Branch، بدون Tool Contract الزاماً هم‌معنا نیستند.

قرارداد Coverage Claim: فیلدهای حداقلی

بخشفیلدهای لازمپرسش ممیزی
PurposeClaim ID/Version، Purpose، Decision، Audienceاین درصد قرار است کدام تصمیم را بهتر کند؟
SubjectProduct، Repository، Commit، Build، Artifact digest، Cutoffعدد دربارهٔ دقیقاً کدام شیء تغییرناپذیر است؟
SemanticsCoverage type، Unit، Covered state، Claim limit«پوشش‌داده‌شده» چه شرطی دارد؟
UniverseSource، Query، Snapshot، Eligibility، Dedup، Digestصورت و مخرج از یک Registry آمده‌اند؟
MeasurementInstrumentation، Collector/version، Config digest، Formula، Roundingتولید عدد قابل‌بازتولید است؟
ExceptionsExclusion، N/A، Unknown، Stale، Invalid با Owner و Expiryچیزی بی‌صدا ناپدید شده است؟
EvidenceRaw/report artifact و digest، Run، زمان، Producer، RetentionDashboard به Source Evidence برمی‌گردد؟
GovernanceThreshold rationale، Guardrail، Review due، Correctionبا تغییر یا کشف خطا چه می‌کنیم؟

قالب آماده Coverage Claim

coverageClaim:
  id: COV-CLAIM-024
  version: 7
  purpose: "دیدن Gap ساختاری تغییر Checkout"
  decision: "اولویت بررسی و افزودن تست؛ نه تصمیم Release"
  audience: [developer, tester, reviewer]
  subject:
    repository: checkout-api
    commit: C24
    build: BUILD-R24
    artifactDigest: sha256:...
  semantics:
    type: branch
    unit: jacoco-branch-counter
    covered: "counter status FULLY_COVERED در همان Build"
    claimLimit: "فقط Reachability؛ نه Oracle، کیفیت یا نبود نقص"
  universe:
    id: UNIV-COV-7
    snapshotAt: 2026-08-13T07:30:00Z
    eligiblePredicate: "production class included by CONFIG-9"
    dedupKey: "class-id + method + branch-index"
    digest: sha256:...
  measurement:
    collector: "JaCoCo pinned-version"
    configDigest: sha256:...
    covered: 581
    denominator: 624
    formula: "100 * covered / denominator"
    percentage: 93.11
    rounding: "half-up, two decimals"
  exceptions:
    excluded: 17
    notApplicable: 9
    unknown: 4
    stale: 3
  evidenceManifest: EVID-COV-24
  owner: quality-capability-owner
  reviewDue: 2026-08-20
  status: READY_FOR_COVERAGE_CLAIM_REVIEW

نام ابزار یا Threshold به‌تنهایی قرارداد نیست. نسخهٔ واقعی باید هویت‌های سازمان شما، مسیر Artifact، سیاست نگهداشت، دسترسی و محدودیت تصمیم را نیز داشته باشد. مقدارهای بالا کاملاً مصنوعی‌اند.

گام اول: Purpose و Decision را قبل از درصد بنویسید

یک درصد ممکن است برای کشف Gap، بازبینی PR، نگه‌داشت Ratchet در Legacy، مقایسهٔ آزمایشی دو Strategy یا مشاهدهٔ Drift استفاده شود. هر Purpose دامنهٔ ادعا و واکنش متفاوتی دارد. «نمایش به مدیریت» Purpose کافی نیست؛ باید معلوم باشد مخاطب پس از دیدن Signal چه تصمیم مجازی می‌تواند بگیرد.

Purposeتصمیم مجازتصمیم غیرمجاز بدون Evidence دیگر
Gap discoveryانتخاب ناحیه برای تحلیلرد خودکار Release
Changed-code guardrailدرخواست بررسی Gap تغییراعلام کیفیت کل محصول
Legacy ratchetجلوگیری از افت تعریف‌شدهتحمیل هدف یکسان به همهٔ Moduleها
Experimentمقایسه در Universe ثابترتبه‌بندی افراد یا تیم‌ها
Risk evidence supportتشخیص Evidence ناقصپذیرش خودکار ریسک

گام دوم: Subject، Build و Cutoff را قفل کنید

Repository name کافی نیست. یک Claim باید Commit، Build ID، digest محصول قابل‌اجرا، Build profile، Source map و زمان Cutoff داشته باشد. Coverage از Run روی BUILD-R23 را نباید به BUILD-R24 نسبت داد؛ حتی اگر نام Branch یکی باشد. Merge، Rebase، Generated source، Optimization و Instrumentation می‌توانند رابطهٔ Source و executable را تغییر دهند.

  • Source identity: Repository، Commit SHA، submodule و generated-source recipe.
  • Artifact identity: Build ID، binary/package digest، compiler/profile.
  • Execution identity: Run ID، Suite version، Environment، Data Pack و زمان.
  • Observation identity: Collector/version، agent mode، config digest و report digest.

گام سوم: Coverage Type و واحد شمارش را صریح کنید

نوعواحد نمونهCovered semantics نمونهمحدودیت مهم
Instructionbytecode instructionProbe مرتبط اجرا شدهبا خط Source یکی نیست
Linesource line قابل نگاشتحداقل Instruction نسبت‌داده‌شده اجرا شدهیک خط می‌تواند Partial باشد
Branchcounter/arc تعریف ابزارمقصد یا Branch مشاهده شدهتعریف Exception و compiler متفاوت است
Function/Methodتابع یا method واجد شرایطحداقل یک Instruction اجرا شدهداخل تابع و Oracle را نشان نمی‌دهد
RequirementRequirement versionEvidence state مطابق قراردادLink با Evidence معتبر یکی نیست
RiskRisk scenario versionEvidence تازه و bound موجود استپوشش با حذف ریسک یکی نیست

برای انتخاب Statement، Branch، Condition و Path بر اساس ساختار کد، مالک موضوع همان آموزش White-box Testing است. اینجا مهم‌تر از نام Counter، قرارداد قابل‌تکرار آن است.

گام چهارم: Coverage Universe Registry بسازید

Universe فهرست نسخه‌دار همهٔ واحدهایی است که پیش از دیدن نتیجه، نامزد مخرج‌اند. ساختن Universe بعد از Run امکان Cherry-picking دارد. Registry باید Source/query، snapshot time، eligible predicate، unit identity، dedup key، digest و Owner داشته باشد.

universeEntry:
  universeId: UNIV-COV-7
  unitId: checkout.PaymentService#confirm:B14
  unitType: jacoco-branch-counter
  sourceRef: src/payment/PaymentService.java@C24
  eligibility: ELIGIBLE
  rationale: production-bytecode-in-scope
  state: NOT_COVERED
  evidenceRef: EVID-COV-24#counter-918
  observedAt: 2026-08-13T07:34:12Z
  owner: checkout-team
  reviewDue: 2026-08-20

برای Requirement یا Risk نیز همین الگو برقرار است؛ Unit ID باید به نسخهٔ Basis یا Risk Registry متصل باشد. نگاشت کلی «REQ-۱۲ → test-payment» بدون Test version، Evidence state و Subject، Coverage معتبر نمی‌سازد.

Eligible Predicate؛ دروازه ورود به مخرج

Predicate باید قابل‌اجرا و پیشینی باشد؛ مثلاً «همهٔ Classهای Production در Artifact با namespace مشخص، به‌جز Entryهای دارای Exclusion فعال». عبارت‌هایی مثل «کدهای مهم»، «موارد قابل‌تست» یا «سناریوهای لازم» بدون Registry و معیار، راه ورود سلیقه به مخرج‌اند.

Predicate ضعیفنسخهٔ ممیزی‌پذیر
فایل‌های مرتبطفایل‌های production تحت paths-v4 در Commit C24
کد Generated را حذف کنartifactهایی با generator ID/version ثبت‌شده و Exclusion EX-۱۷ فعال
نیازمندی‌های مهمRisk tier ۱–۲ در RISK-v8 تا Cutoff مشخص
مرورگرهای لازمConfig tupleهای فعال در MATRIX-v5 با rationale و expiry

Dedup؛ چرا یک واحد نباید چند بار رأی بدهد؟

Merge چند Report، اجرای موازی، Retry و چند Test layer ممکن است یک Unit را چند بار مشاهده کند. صورت باید «تعداد واحد یکتای پوشش‌داده‌شده» باشد، نه تعداد hit یا تعداد Test. Dedup key باید قبل از Merge تعریف شود. جمع‌زدن ۲۰۰ Branch از Shard اول و ۲۰۰ Branch از Shard دوم، وقتی ۱۵۰ Branch مشترک‌اند، ۴۰۰ واحد Coverage نمی‌سازد.

وضعیت‌های لازم: Covered فقط یکی از آن‌هاست

Stateمعنارفتار در محاسبه
COVEREDCovered semantics برای Subject همان Claim برقرار استدر صورت و مخرج
NOT_COVEREDواجد شرایط است اما Evidence پوشش نداردفقط در مخرج
PARTIALبخشی از Unit مرکب مشاهده شدهطبق قرارداد؛ هرگز خودکار Full
NOT_APPLICABLEقاعدهٔ applicability برای این Subject صدق نمی‌کندخارج از مخرج با rationale
EXCLUDEDبا تصمیم حاکمیتی موقت/دائم کنار گذاشته شدهجداگانه گزارش شود
UNKNOWNامکان تعیین وضعیت نیستپنهان نشود؛ Claim را محدود کند
STALEEvidence به نسخه/زمان معتبر تعلق نداردCovered جاری محسوب نشود
INVALIDEvidence یا اندازه‌گیری قواعد را نقض کردهاز Claim معتبر حذف و Incident ثبت شود

Exclusion با Not Applicable یکی نیست

NOT_APPLICABLE یعنی خود قاعدهٔ Applicability دربارهٔ این Subject برقرار نیست. EXCLUDED یعنی واحد بالقوه در Scope بوده اما بر اساس Policy از اندازه‌گیری کنار گذاشته شده است. Generated code، Vendor library، Migration، fallback، unreachable code و Feature Flag خاموش به‌طور خودکار هیچ‌کدام N/A یا Excluded نیستند؛ هرکدام به تحلیل و ثبت نیاز دارند.

exclusion:
  id: EX-COV-017
  universeId: UNIV-COV-7
  unitIds: [generated.client.*]
  policyId: COV-POL-4
  rationale: "منبع از schema نسخه‌دار تولید می‌شود"
  compensatingEvidence: generator-contract-run/E-72
  requestedBy: build-owner
  reviewedBy: independent-reviewer
  effectiveFrom: 2026-08-13T00:00:00Z
  expiresAt: 2026-09-13T00:00:00Z
  status: ACTIVE

فایل config که با یک wildcard نیمی از Repository را حذف می‌کند، Evidence تصمیم نیست. تعداد و سهم Exclusion باید کنار درصد اصلی دیده شود؛ وگرنه بهینه‌سازی عدد با کوچک‌کردن مخرج تشویق می‌شود.

Unknown و Stale را صفر نکنید

Collector crash، کلاس‌گذاری پویا، Source map ناقص، Run نیمه‌تمام، Artifact foreign یا Mapping نامشخص «Not covered» نیستند. این‌ها Unknown یا Invalid هستند. همچنین Evidence مربوط به Commit قبلی Stale است، حتی اگر رفتار ظاهراً تغییر نکرده باشد. تبدیل Unknown به Covered عدد را خوش‌رنگ و Claim را غیرقابل‌اعتماد می‌کند؛ تبدیل آن به Not covered نیز علت فنی را پنهان می‌سازد.

Instrumentation Contract؛ ابزار دقیقاً چه چیزی را دید؟

  • نام و نسخهٔ Collector/Agent و حالت on-the-fly یا offline را Pin کنید.
  • Include/Exclude، branch flag، plugin، compiler، optimization و debug info را digest کنید.
  • رابطهٔ Source، generated code، bytecode/binary و Source map را ثبت کنید.
  • پوشش Processهای child، worker، container، dynamic module و remote service را روشن کنید.
  • نحوهٔ Merge فایل‌های execution data، Retry و Shard را تعریف کنید.
  • Collector failure، empty report، partial upload و version mismatch را FAIL/INVALID کنید؛ نه صفر یا ۱۰۰٪.

هدف، مقدس‌کردن ابزار نیست؛ هدف این است که بعداً یک Reviewer بتواند بفهمد Counter از کجا آمده است. ابزار فقط Observation می‌دهد و تصمیم‌گیر Coverage Contract است.

Coverage Evidence Manifest

evidenceManifest:
  id: EVID-COV-24
  subjectId: checkout-api@C24
  buildId: BUILD-R24
  runId: RUN-COV-24
  universeId: UNIV-COV-7
  collector: jacoco/pinned-version
  configDigest: sha256:...
  rawArtifact: artifacts/RUN-COV-24/jacoco.exec
  rawDigest: sha256:...
  reportArtifact: artifacts/RUN-COV-24/report.xml
  reportDigest: sha256:...
  startedAt: 2026-08-13T07:31:00Z
  finishedAt: 2026-08-13T07:35:00Z
  producer: pipeline/COV-9
  unknowns: [dynamic-plugin-P17]
  redaction: no-user-or-production-payload
  retention: 90d

Screenshot داشبورد، Source Evidence نیست؛ تصویر نه query و config را حفظ می‌کند، نه Counterهای خام را و نه امکان بازحساب دارد. Dashboard باید به Manifest و Artifact تغییرناپذیر لینک شود. برای اصول نمایش عدد بدون حذف Context، راهنمای Chart Contract و Visual QA مفید است.

فرمول، گردکردن و مخرج صفر

Coverage معمولاً از covered/total به دست می‌آید، اما قرارداد باید شمارش Partial، Unknown و واحد مرکب را مشخص کند. گردکردن فقط برای نمایش است. مقدار ۹۹٫۹۹۶٪ با گردکردن دو رقم ممکن است ۱۰۰٫۰۰٪ دیده شود، ولی Full coverage نیست. در Gate باید Counter خام یا شرط missedCount = ۰ استفاده شود.

if denominator == 0:
  result = EMPTY_UNIVERSE       # نه 100 و نه 0
elif invalidEvidence:
  result = INVALID
else:
  exactRatio = covered / denominator
  displayPercent = round(exactRatio * 100, 2)
  fullCoverage = (missed == 0 and partial == 0 and unknown == 0)

چرا میانگین درصدها اغلب اشتباه است؟

Module A با ۹۰/۱۰۰ برابر ۹۰٪ و Module B با ۱/۲ برابر ۵۰٪ است. میانگین ساده ۷۰٪ می‌شود، اما تجمیع درست Counterها ۹۱/۱۰۲ یا ۸۹٫۲۲٪ است. اگر Universeها overlap داشته باشند، حتی جمع Counter نیز قبل از Dedup معتبر نیست. وزن‌دهی Risk یک تصمیم سیاستی جداست و نباید با Coverage ساختاری خام مخلوط شود.

روشنتیجهاعتبار
(۹۰٪ + ۵۰٪) ÷ ۲۷۰٪نامعتبر؛ اندازهٔ مخرج نادیده گرفته شده
(۹۰ + ۱) ÷ (۱۰۰ + ۲)۸۹٫۲۲٪فقط اگر Unit و Universe سازگار و بدون overlap باشند
Risk-weighted scoreوابسته به وزنSignal جدا با مدل/نسخه؛ نه جایگزین Counter خام

Denominator Drift؛ وقتی درصد عوض می‌شود اما تست نه

اگر کد جدید اضافه، dead code حذف، Generator عوض، Requirement split یا Risk retire شود، مخرج Drift می‌کند. ممکن است تعداد Covered ثابت بماند ولی درصد بالا یا پایین برود. هر مقایسه باید numerator delta، denominator delta، علت تغییر Universe و Comparability را کنار هم نشان دهد.

BaselineCurrentظاهرتفسیر درست
۸۰۰/۱۰۰۰ = ۸۰٪۸۰۰/۸۵۰ = ۹۴٫۱۲٪بهبود ۱۴٫۱۲ واحد۱۵۰ Unit از مخرج خارج شده؛ Coverage جدیدی ثبت نشده
۸۰۰/۱۰۰۰ = ۸۰٪۸۶۰/۱۱۰۰ = ۷۸٫۱۸٪افت ۱٫۸۲ واحد۶۰ Covered جدید و ۱۰۰ Unit جدیدِ باز وجود دارد
۵۸۰/۶۲۰۵۸۱/۶۲۴درصد نزدیکنیازمند بررسی ۴ Unit جدید، یک Covered جدید و تعریف ثابت

Comparability Decision؛ آیا دو گزارش قابل مقایسه‌اند؟

  • COMPARABLE: Unit، Semantics، Collector/config و Universe policy ثابت‌اند؛ Drift توضیح داده شده است.
  • RESTATED_BASELINE: با دادهٔ خام، Baseline تحت تعریف جدید دوباره محاسبه شده است.
  • DIRECTIONAL_ONLY: اختلاف محدود وجود دارد و فقط جهت تقریبی قابل‌استفاده است.
  • NOT_COMPARABLE: تعریف، ابزار، Scope یا Evidence چنان عوض شده که Trend معتبر نیست.

آیا پوشش تست ۱۰۰٪ بد است؟

خیر. ۱۰۰٪ می‌تواند نتیجه‌ای درست و مفید برای یک Universe محدود باشد؛ مثلاً تمام Branchهای یک تابع کوچک و حساس در Build مشخص مشاهده شده‌اند. خطا زمانی رخ می‌دهد که از این گزارهٔ محدود به «همهٔ سناریوها تست شده»، «کیفیت عالی است»، «هیچ باگی نداریم» یا «Release امن است» جهش کنیم.

ادعای مجاز نمونهادعای نامجاز
۶۲۴/۶۲۴ Counter واجد شرایط طبق UNIV-۷ در BUILD-R24 مشاهده شدتمام کد و تمام رفتارها درست‌اند
missedCount در این Report صفر استهیچ مسیر اجرایی ممکنی باقی نمانده
Line coverage ابزار مشخص ۱۰۰٪ استBranch، Condition، Requirement و Risk هم ۱۰۰٪ است
این Signal از Threshold عبور کردهمحصول Release-ready است

چرا ۱۰۰٪ Line می‌تواند باگ واضح را نبیند؟

function payable(amount, fee) {
  return Math.min(amount + fee, 10_000_000);
}

// خط اجرا می‌شود، اما هیچ خروجی بررسی نمی‌شود:
test('executes', () => { payable(900_000, 50_000); });

این Check می‌تواند خط را Reach کند، ولی اگر جمع به تفریق تغییر کند ممکن است همچنان Pass شود. Coverage پاسخ می‌دهد «کجا مشاهده شد؟»؛ Oracle پاسخ می‌دهد «چه رفتاری درست است؟»؛ Mutation می‌تواند بخشی از حساسیت Test Suite به تغییر را آزمایش کند. هیچ‌کدام به‌تنهایی تمام کیفیت محصول را اثبات نمی‌کنند.

درصد مناسب Code Coverage چند است؟

عدد جادویی عمومی وجود ندارد. ۸۰٪ و ۹۰٪ می‌توانند Policy محلی باشند، نه حقیقت صنعت. Threshold را با Purpose، Criticality، معماری، Legacy baseline، Change surface، هزینهٔ نگهداشت و مکمل‌های Evidence تعیین کنید. برای منطق مالی کوچک ممکن است missed critical branch = ۰ مهم‌تر از Overall ۹۵٪ باشد؛ برای Adapter تولیدشده، Contract evidence شاید از تعقیب Line بیشتر ارزش داشته باشد.

جزء Policyنمونهٔ قابل‌دفاع
Purposeجلوگیری از ایجاد Gap جدید در کد تغییرکرده
SignalChanged-branch coverage تحت تعریف ثابت
Hard constraintهیچ Critical risk branch ثبت‌شده بدون disposition نماند
ThresholdRatchet نسبت به Baseline بازحساب‌شده؛ نه عدد کپی‌شده
Guardrailزمان Suite، Flakiness، Assertion review و Exclusion share
ExceptionOwner، rationale، compensating evidence و expiry
Claim limitSignal بازبینی؛ نه Release یا Quality verdict

Goodhart و بازی متریک: وقتی هدف، مخرج را می‌خورد

اگر پاداش یا ارزیابی افراد به درصد متصل شود، رفتار منطقی سیستم می‌تواند افزودن Checkهای کم‌ارزش، حذف فایل، شکستن Unit، Mock بیش‌ازحد، Retry تا Green یا انتخاب ابزار با Counter مساعد باشد. مقابله فقط با «فرهنگ بهتر» ممکن نیست؛ Metric باید محدودیت ادعا، Guardrail و منع رتبه‌بندی داشته باشد.

  • درصد را برای تشخیص کار استفاده کنید، نه ارزیابی فرد.
  • Numerator، denominator، Exclusion، Unknown و Drift را هم‌زمان نمایش دهید.
  • افزایش Coverage را کنار Mutation sample، Oracle review، Suite health و Cycle time ببینید.
  • هر Exception را منقضی و هر تغییر config را Review کنید.
  • بهبود عدد بدون بهبود تصمیم یا کاهش Unknown را موفقیت اعلام نکنید.

Coverage Gap یک دستور «تست بنویس» نیست

GapDisposition محتملEvidence لازم
رفتار مهم بدون مشاهدهافزودن Test در Layer مناسبRisk/Basis و Oracle
کد unreachable واقعیحذف یا Refactorتحلیل control flow و review
Generated adapterContract test یا Exclusion محدودGenerator identity و compensating evidence
Third-party boundaryIntegration/contract evidenceSandbox/fidelity limitation
Measurement failureRepair CollectorIncident و rerun
ریسک پذیرفته‌شدهAccept/defer زمان‌دارDecision owner، expiry و impact

Layer را صرفاً برای بالا بردن عدد انتخاب نکنید. راهنمای هرم تست و طراحی سبد Unit، Integration و E2E کمک می‌کند Observation را در نزدیک‌ترین Layer توانمند و Evidence نماینده‌تر تکمیل کنید. برای اولویت‌گذاری نیز مالک موضوع تست مبتنی بر ریسک است.

Overall، Changed Code و Diff Coverage

Overall Coverage دربارهٔ Universe بزرگ‌تری از محصول است؛ Diff/Changed-code Coverage دربارهٔ واحدهای منتسب به تغییر. دومی برای Feedback سریع مفید است، اما تغییر config، schema، dependency، feature flag یا رفتار کد قدیمی ممکن است در Diff خطی دیده نشود. هر دو Claim باید Universe و attribution rule مستقل داشته باشند و یکی جای دیگری را نگیرد.

Dashboard چه چیزی را باید کنار درصد نشان دهد؟

لایهنمایش حداقلی
IdentitySubject، Commit، Build، Run، Universe، Collector/config
CountersCovered، missed، partial، denominator و درصد بدون گردکردن فریبنده
ValidityUnknown، stale، invalid، empty، report completeness
ScopeIncluded، excluded، N/A و denominator drift
ComparisonBaseline، numerator delta، denominator delta، comparability
DecisionPurpose، threshold rationale، guardrail، exception و claim limit
EvidenceManifest، raw/report digest، producedAt و retention

گزارش Status یا Release باید این Signal را در کنار Evidenceهای دیگر مصرف کند، نه اینکه آن را به Verdict نهایی تبدیل کند. برای این مرزبندی، راهنمای گزارش تست و تصمیم انتشار را ببینید.

Correction؛ اگر بعداً فهمیدیم درصد غلط بوده چه کنیم؟

Coverage Claim دادهٔ زندهٔ تصمیم است و باید اصلاح‌پذیر باشد. کشف config اشتباه، Artifact foreign، mapping ناقص، Exclusion نامعتبر یا formula bug فقط با اصلاح Dashboard تمام نمی‌شود؛ باید Claimهای متاثر، تصمیم‌های مصرف‌کننده و Baselineهای Trend شناسایی شوند.

correction:
  id: CORR-COV-009
  detectedAt: 2026-08-13T09:10:00Z
  cause: "گزارش BUILD-R23 به Claim BUILD-R24 متصل شده بود"
  invalidEvidence: EVID-COV-23
  affectedClaims: [COV-CLAIM-024, DASH-SNAPSHOT-88]
  affectedDecisions: [REVIEW-421]
  containment: "Claimها INVALID و مصرف downstream متوقف شد"
  recalculation: RUN-COV-24R1
  correctedResult: 93.11
  republishedAt: 2026-08-13T10:20:00Z
  notified: [claim-owner, review-owner]
  prevention: "build digest equality gate"
  status: CLOSED

آزمایشگاه مصنوعی Checkout فارسی

برای جلوگیری از هرگونه ادعای واقعی، این آزمایشگاه کاملاً جدا و ساختگی است: `SYN-CHECKOUT` با Order، PaymentAttempt، PSP Stub، Callback، Ledger و Reconciliation جعلی. هیچ شبکه، Production، شرکت، کاربر، بانک، PSP واقعی، نام، موبایل، ایمیل، IP، حساب، PAN، CVV2، OTP، Cookie، Token، Credential، Screenshot یا Log واقعی در آن وجود ندارد.

بعدواحدهای مصنوعیریسک تفسیر
MoneyIRR خیالی canonical؛ تومان فقط presentation و برچسب‌دارتبدیل نمایشی با منطق Ledger اشتباه نشود
Digitsارقام فارسی، عربی و لاتینLine coverage تفاوت parsing را ثابت نمی‌کند
Unicode/RTLNFC، RTL/LTR و stable IDمشاهدهٔ Branch با صحت نمایش یکی نیست
TimeUTC instant، Asia/Tehran و جلالی صرفاً نمایشیاجرای مسیر، صحت cutoff را اثبات نمی‌کند
Statetimeout قبل/بعد fake commit، Callback تکراری/دیر/جابجا۱۰۰٪ Statement تمام interleavingها را پوشش نمی‌دهد
IsolationTenant/Order/Attempt/Event/Ledger/Run/Build/Evidence ID جداMerge بین Buildها Claim را Invalid می‌کند

این Lab هیچ ادعای بانکی، مالی، حقوقی، مالیاتی، امنیتی، حریم خصوصی یا انطباق دربارهٔ ایران ندارد. هدف فقط نمایش خطای استنتاج از Reachability به Correctness است.

آزمایش اجرایی SYN-COVERAGE-CLAIM-DENOMINATOR-۰۱

یک Validator مستقل، dependency-free و قطعی با Node.js v26.۷.۰ اجرا شد. داشبورد سطحی ۶۲۴/۶۲۴ واحد، ۳۸۰/۳۸۰ تست Pass و ۱۰۰٪ Requirement mapped نشان می‌داد و Verdict آن PASS بود. Validator به‌جای اعتماد به عدد، ۱۴۰ قاعده را روی Contract اعمال کرد.

گروه قاعدهتعدادنمونه کنترل
Identity۲۰Product، Risk، Basis، Commit، Build digest، Run، Collector
Claim۱۵Purpose، Decision، type، unit، semantics، claim limit، Owner
Universe۲۴Source/query/snapshot، eligibility، dedup، exclusion، Unknown، digest
Measurement۲۰Instrumentation، Counterها، formula، rounding، aggregation، time
Evidence۱۶Subject/Build/Run binding، raw/report artifact و digest، retention
Governance۱۵Threshold rationale، drift، comparability، exception، Correction
ادعاهای ممنوع۲۵۱۰۰٪=کیفیت/بدون نقص/Release، هدف عمومی، ranking، AI approval
روابط بین‌فیلدی۵صورت≤مخرج، بازتولید درصد، Build/Universe/time binding

نسخهٔ سطحی با ۱۳۳ Finding به HOLD رفت. هفت قاعده واقعاً Pass بودند: سه Counter و درصد ارائه شده بود و چهار رابطهٔ محدود به‌علت نبود داده به شکل مورد انتظار Validator نقض نشده بود؛ بنابراین برای ساختن عدد نمایشی، شمارش Findings دست‌کاری نشد. Findings شامل هویت‌های مفقود، Claim بی‌Purpose، Universe بدون Registry، نبود config/raw digest، Exclusion و Unknown نامعلوم، Threshold بی‌منطق، نبود Correction و ۲۵ استنتاج مطلق بود.

fixture: SYN-COVERAGE-CLAIM-DENOMINATOR-01
rules: 140

superficial dashboard:
  covered/denominator: 624/624
  tests passed: 380/380
  requirements mapped: 100%
  dashboard verdict: PASS
  contract verdict: HOLD
  findings: 133

corrected contract:
  covered/denominator: 581/624
  display percentage: 93.11%
  missed: 31
  partial: 12
  excluded: 17
  notApplicable: 9
  unknown: 4
  stale: 3
  structural findings: 0
  status: READY_FOR_COVERAGE_CLAIM_REVIEW

در نسخهٔ اصلاح‌شده، Subject و BUILD-SYN-۲۴، Universe نسخه‌دار UNIV-SYN-۷، Collector/config، Counterها، Evidence، Drift، Exception و Correction کامل و روابط بین‌فیلدی برقرار شدند. خروجی `READY_FOR_COVERAGE_CLAIM_REVIEW` عمداً فقط آمادگی ساختاری Claim برای بازبینی است؛ نه صدق Source یا Evidence، نه کفایت تست، نه قدرت Oracle، نه نبود نقص، نه کیفیت محصول و نه آمادگی انتشار.

پیاده‌سازی در CI/CD بدون ساختن Gate کور

collect raw execution data
  -> verify subject/build/config identity
  -> materialize versioned universe
  -> apply eligibility + dedup
  -> classify every unit state
  -> validate evidence completeness
  -> calculate raw counters and display percentage
  -> assess denominator drift + comparability
  -> apply contextual threshold + guardrails
  -> publish claim + manifest + limitations
  -> preserve correction path

اگر هر مرحله Unknown یا Invalid است، Pipeline نباید یک عدد قبلی را به‌عنوان Current بازنشر کند. «Fail closed» همیشه به معنی Block Release نیست؛ یعنی وضعیت Claim را صادقانه INVALID/INCONCLUSIVE اعلام کنید و تصمیم Release را به Policy و Evidenceهای مستقل بسپارید.

Threshold، Guardrail و Exception را با هم طراحی کنید

SignalCountermetric/Guardrailچرا لازم است؟
Coverage deltaDenominator delta و Exclusion shareبهبود صوری با کوچک‌کردن مخرج آشکار شود
Changed coverageConfig/schema/dependency impact reviewتغییر غیرخطی از قلم نیفتد
Suite sizeDuration، flakiness و maintenance costانباشت Check کم‌ارزش پاداش نگیرد
Mutation sampleEquivalent/invalid mutant reviewScore خام به کفایت تبدیل نشود
Requirement mappingfresh valid evidence stateLink تزئینی پوشش محسوب نشود
Threshold passCritical gap و Unknown hard constraintsمیانگین، شکاف مهم را نپوشاند

اتوماسیون و AI کجا کمک می‌کنند و کجا نه؟

اتوماسیون می‌تواند Manifest بسازد، digest تطبیق دهد، Drift را محاسبه کند، Entry بدون Owner را بیابد و Claim را بازتولید کند. AI می‌تواند Gapها را خوشه‌بندی، توضیح اولیه یا Candidate disposition پیشنهاد کند؛ اما نباید Exclusion، N/A، پذیرش Risk، تغییر Threshold یا Release را خودکار تأیید کند. هر پیشنهاد باید Source، confidence، Unknown، Reviewer و audit log داشته باشد.

  • هیچ کد، Prompt یا Artifact حساس را به سرویس تأییدنشده ارسال نکنید.
  • دادهٔ کاربر/Production را برای ساخت مثال Coverage وارد نکنید.
  • پیشنهاد AI را Evidence یا approval حساب نکنید.
  • نسخهٔ مدل، Prompt/Policy و خروجی پذیرفته/ردشده را در محدودهٔ مجاز ثبت کنید.
  • مسیر دستی و حق اعتراض به Exclusion یا ارزیابی تیم حفظ شود.

۳۰ ضدالگوی رایج پوشش تست

ضدالگوخطراصلاح
۱۰۰٪ بدون نوع Counterابهام معناییType/Unit/Semantics
نام Repository بدون CommitSubject شناورCommit/Build/digest
Dashboard بدون Manifestغیرقابل‌بازتولیدraw/report artifacts
Screenshot به‌جای Evidenceقطع lineageلینک به Artifact
جمع hitهاتکرار صورتunique unit + dedup
میانگین درصد Moduleهانادیده‌گرفتن اندازهCounter aggregation
گردکردن ۹۹٫۹۹۶ به FullFull کاذبmissed/partial raw
مخرج صفر برابر ۱۰۰موفقیت تهیEMPTY_UNIVERSE
Unknown برابر Coveredاطمینان کاذبUNKNOWN مستقل
Stale برابر Currentنسبت‌دادن Evidence قدیمیfreshness rule
Excluded برابر N/Aپنهان‌کردن تصمیمState جدا
wildcard exclusionکوچک‌شدن بی‌صدای مخرجRegistry/owner/expiry
Generated همیشه حذفاز دست‌رفتن ریسک Generatorتحلیل + compensating evidence
Unreachable همیشه بی‌خطرdead/defensive code مبهمحذف، Refactor یا rationale
Pass rate برابر Coverageاختلاط Result و ReachSignalهای جدا
Mapped برابر TestedLink تزئینیEvidence state
Executed برابر AssertedOracle ضعیفOracle review
۱۰۰٪ برابر همهٔ Pathهااستنتاج بیش‌ازحدclaim limit
۱۰۰٪ برابر بدون نقصQuality claim بی‌مدرکEvidence مکمل
Threshold عمومی ۸۰/۹۰Policy بدون Contextrationale/baseline
Threshold تنها GateCritical gap پنهانhard constraints
More is always betterهزینه و نگهداشتValue + guardrail
Diff به‌جای OverallRisk خارج از خطوطClaimهای مکمل
Overall به‌جای ChangeGap PR محو می‌شودchanged-universe claim
Trend با Tool جدیدمقایسهٔ نامعتبرrestated baseline
Retry فقط آخرین attemptپنهان‌کردن ناپایداریattempt evidence
رتبه‌بندی تیم‌هابازی و بی‌عدالتیتشخیص سیستم، نه فرد
AI-approved exclusionواگذاری اختیارhuman accountable review
اصلاح Dashboard بدون Impactتصمیم‌های قدیمی آلودهaffected-claim analysis
Coverage به‌عنوان Release verdictفروکاست تصمیمگزارش چندEvidence‌ای

چک‌لیست ممیزی Coverage Claim

  1. Claim ID و Version یکتا دارد؟
  2. Purpose، Decision و Audience روشن‌اند؟
  3. Product، Repository، Commit، Build و digest قفل‌اند؟
  4. Coverage type، Unit و Covered semantics تعریف شده‌اند؟
  5. Claim limit از Quality و Release جداست؟
  6. Universe ID/version/source/query/snapshot موجود است؟
  7. Eligible predicate پیشینی و قابل‌اجراست؟
  8. Unit identity و Dedup key ثابت‌اند؟
  9. Included manifest و digest نگه‌داری شده‌اند؟
  10. Exclusion registry، rationale، Owner و Expiry دارد؟
  11. N/A از Excluded جداست؟
  12. Unknown، Stale و Invalid صریح‌اند؟
  13. Generated، vendor، unreachable، dynamic و feature flag Policy دارند؟
  14. Collector/version و instrumentation mode Pin شده‌اند؟
  15. Config، raw execution و report digest موجود است؟
  16. Covered، denominator، missed و partial خام دیده می‌شوند؟
  17. Formula، rounding، aggregation و empty rule ثبت شده‌اند؟
  18. صورت از مخرج بزرگ‌تر نیست و درصد بازتولید می‌شود؟
  19. Evidence به Subject، Build، Run و Universe همان Claim متصل است؟
  20. زمان، Producer، Unknown، retention و redaction معلوم‌اند؟
  21. Denominator drift و علت تغییر گزارش شده است؟
  22. Comparability با Baseline تصمیم‌گیری شده است؟
  23. Threshold rationale و Guardrail وجود دارد؟
  24. Gap disposition و Exception زمان‌دارند؟
  25. Correction، affected claims و republish path آزموده شده‌اند؟
  26. درصد برای رتبه‌بندی فرد/تیم استفاده نمی‌شود؟
  27. AI فقط پیشنهاد می‌دهد و اختیار انسانی حفظ شده است؟
  28. Dashboard به Evidence خام لینک و محدودیت را نمایش می‌دهد؟

برنامه ۳۰ روزه برای اصلاح Coverage Reporting

بازهکارخروجی قابل‌تحویل
روز ۱–۵انتخاب یک Claim و ثبت Purpose/Subject/ToolBaseline و فهرست Unknown
روز ۶–۱۰ساخت Universe Registry و طبقه‌بندی Exclusion/N/AUniverse digest و denominator قابل‌بازتولید
روز ۱۱–۱۵اتصال raw/report Artifact و اعتبارسنجی روابطEvidence Manifest و Validator
روز ۱۶–۲۰طراحی Threshold، Guardrail و Gap dispositionPolicy نسخه‌دار و Exception workflow
روز ۲۱–۲۵نمایش Drift/Comparability و اجرای Correction drillBaseline restatement یا NOT_COMPARABLE
روز ۲۶–۳۰Pilot در یک Repository و بازبینی تصمیم‌هاKeep/Adapt/Stop و Backlog محدود

موفقیت Pilot را با رسیدن به درصد بالاتر نسنجید. معیار بهتر این است که آیا یک Reviewer مستقل توانست Claim را بازتولید کند، Unknownها کمتر شدند، Exclusion بی‌مالک نماند، Drift توضیح داده شد و یک تصمیم اشتباه پیش از مصرف اصلاح شد.

جمع‌بندی: درصد را دور نریزید؛ ادعایش را محدود کنید

پوشش تست ۱۰۰٪ سراب نیست؛ یک مشاهدهٔ محدود است که بدون قرارداد می‌تواند شبیه سراب مصرف شود. عدد زمانی ارزش دارد که مخرج آن نسخه‌دار، صورت آن یکتا، Semantics آن روشن، Evidence آن هم‌Build، استثناهایش آشکار و Correction آن عملی باشد. آن‌وقت ۱۰۰٪ می‌گوید «در Universe تعریف‌شده Gap مشاهده‌نشده نداریم»—و صادقانه سکوت می‌کند دربارهٔ کیفیت، Oracle، سناریوهای غایب، نقص‌های ناشناخته و Release.


سوالات متداول درباره پوشش تست ۱۰۰٪

آیا پوشش تست ۱۰۰٪ یعنی نرم‌افزار بدون باگ است؟

خیر. ۱۰۰٪ فقط می‌تواند بگوید تمام واحدهای واجد شرایط در Universe و تحت Covered semantics مشخص مشاهده شده‌اند. این عدد درستی Oracle، کامل‌بودن Basis، همهٔ ورودی‌ها، concurrency، امنیت، کارایی، تجربهٔ کاربر یا نبود نقص را اثبات نمی‌کند.

درصد مناسب Code Coverage برای پروژه چقدر است؟

عدد عمومی وجود ندارد. Threshold باید از Purpose، Risk، Criticality، معماری، Baseline، هزینه و Evidence مکمل ساخته شود. به‌جای کپی ۸۰٪ یا ۹۰٪، hard constraintهای Critical، Ratchet کد تغییرکرده و Guardrailهای سلامت Suite را تعریف کنید.

تفاوت Line Coverage و Branch Coverage چیست؟

Line Coverage مشاهدهٔ واحدهای خط را طبق تعریف ابزار می‌سنجد؛ Branch Coverage مقصدها یا Counterهای تصمیم را. اجرای تمام خطوط الزاماً هر خروجی تصمیم را نمی‌بیند. تعریف دقیق نیز ابزارمحور است؛ پس نام ابزار، نسخه، config و Covered semantics را ثبت کنید.

آیا موارد Excluded را باید از مخرج حذف کنیم؟

فقط طبق Policy نسخه‌دار و با Entry شامل Unit، rationale، Owner، Reviewer، compensating evidence و Expiry. سهم Exclusion باید کنار درصد دیده شود. Excluded را با NOT_APPLICABLE یا UNKNOWN یکی نکنید و تغییر آن را Denominator Drift گزارش دهید.

آیا Mutation Testing جایگزین Code Coverage است؟

خیر. Code Coverage بیشتر Reachability و Mutation Testing حساسیت نسبت به مجموعه‌ای از تغییرهای مصنوعی را بررسی می‌کند. هر دو محدودیت و مخرج خود را دارند و در کنار Oracle review، Risk evidence و Test portfolio معنا پیدا می‌کنند؛ هیچ‌کدام Verdict کیفیت یا Release نیستند.

دیدگاهتان را بنویسید