20 min read · 9 self-checks · Updated June 2026

White Box · Structural

Condition Coverage

Condition coverage requires that each individual boolean sub-expression within a compound decision is evaluated as both true and false at least once. It is stronger than branch coverage for compound conditions — and the foundation of MC/DC, the coverage standard for safety-critical software.

Senior Test Lead ISTQB CTFL 4.3.3 · CTAL-TA 4.3

1 The Hook

A NZ insurer builds a quote engine. A driver qualifies for the safe-driver rate only when age >= 25 && noClaims. The team writes two tests: a 30-year-old with no claims (qualifies), and an 18-year-old with claims (does not). Both tests pass, and the report shows 100% branch coverage — the decision has been seen both true and false. Sign-off.

What no test ever did was check a 30-year-old with a claim. The noClaims condition was only ever evaluated as true; its false value was never exercised on its own. A developer had typed || where they meant &&, and the bug only shows when one condition is true and the other false. Branch coverage was happy — the overall decision had gone both ways — but the individual condition that hid the fault was never tested both ways.

That is the gap condition coverage closes. A compound decision can reach both overall outcomes without each atomic condition inside it being exercised true and false. One condition can quietly dominate the result and mask a bug in another.

💬
Senior Engineer Insight

The cruelest thing about condition coverage is that your coverage tool will lie to you. I have seen Istanbul and JaCoCo report 100% condition coverage on a compound AND expression where one condition was never evaluated at all — because short-circuit evaluation killed it every time, and the tool counted the line as covered. The tool sees the condition in the source; it does not see that the runtime skipped it. On a NZ health portal I reviewed, a dosing guard had this exact problem: patientActive && weightOk && noContraindication — the first condition was always false in test data, so the other two conditions never ran. Three conditions, zero independent tests. Always verify coverage with dynamic analysis, not line counting.

2 The Rule

Condition coverage requires each atomic boolean condition inside a compound decision to be evaluated both true and false at least once — not just the overall decision. It does not subsume branch coverage; for real assurance combine both (condition/decision coverage) or use MC/DC, where each condition is shown to independently change the outcome.

3 The Analogy

Analogy

A flat that needs two keys to open the front door.

A student flat in Dunedin has a front door with two locks — a deadbolt and a latch — and the door only opens when both are unlocked. Checking that the door opens (both keys work) and that it stays shut when you forget your keys (neither works) is branch coverage: the door has gone both ways. But you have never checked the deadbolt key on its own, or the latch key on its own. If the latch key is secretly a dud, you would never know — the deadbolt being locked kept the door shut anyway.

Condition coverage is testing each key by itself: deadbolt-only, latch-only, so every lock is proven to work and to fail independently. MC/DC goes one step further — it proves that turning each single key, on its own, actually changes whether the door opens.

Senior engineer insight

The thing that changed how I think about condition coverage: coverage tools lie to you via short-circuit evaluation. Istanbul and JaCoCo will happily report 100% condition coverage on a compound AND expression where one of the conditions was never evaluated at runtime — because the tool sees it in the source, not whether the runtime actually reached it. The moment I understood that, I stopped trusting a percentage and started reading dynamic analysis traces instead.

The second shift: I stopped treating condition coverage as a checkbox and started using it as a fault-model argument. Every atomic condition is a place a developer can type the wrong operator or invert a boolean. Condition coverage is your proof that each of those fault sites was exercised both ways.

Most common mistake: writing two tests that vary only one condition and declaring "full condition coverage" — while the second condition sits at the same value in both tests, never tested the other way.

From the field

On a NZ financial services project I was brought in to review, a KiwiSaver withdrawal eligibility function had a compound guard: if (membersince >= 5 && nzResident && age >= 65). The CI pipeline showed 94% branch coverage and the team was confident. When I ran a condition-coverage analysis, I found that nzResident had been true in every test case in the suite — it had never been evaluated false in three years of test writing. A residency-check bug introduced six months earlier had silently passed every build. The fix was four extra test cases and a policy: any compound decision controlling a financial entitlement must have an MC/DC table attached to the test plan before the story closes. That single rule eliminated an entire class of audit finding on the project.

What it is

In most real code, decision points are not simple single conditions — they are compound expressions. if (age >= 18 && hasLicence) contains two atomic conditions: age >= 18 and hasLicence. Branch coverage only cares whether the overall decision evaluates to true or false. Condition coverage goes further: it requires each atomic condition to independently take both a true and a false value across the test suite.

This distinction matters because a compound condition can reach both overall outcomes (true/false) without every atomic condition being exercised on both sides. A bug in one condition might be masked by the other condition always dominating the result.

Atomic condition: a sub-expression within a compound boolean decision that cannot be decomposed further without breaking the boolean nature of the expression. In (A && B) || C, the atomic conditions are A, B, and C. The expressions (A && B) and (A && B) || C are not atomic — they are compound.

Condition coverage vs. branch coverage

Consider this decision: if (A && B)

Branch coverage requires:

  • One test where the overall decision is true: A=true, B=true → overall true
  • One test where the overall decision is false: A=false, B=anything → overall false

With these two tests, B has only ever been evaluated as true (in the first test where B=true) and as “don’t care” (short-circuit evaluation means B may not even be evaluated in the second test). A bug in the B=false branch of the implementation is never triggered.

Condition coverage additionally requires:

  • A evaluated as true at least once
  • A evaluated as false at least once
  • B evaluated as true at least once
  • B evaluated as false at least once

This forces at least one test where B=false is actually evaluated, catching the bug that branch coverage misses.

Important: condition coverage does not subsume branch coverage. It is possible to achieve 100% condition coverage while not covering both branches of a decision. In practice, teams require condition/decision coverage (both criteria combined) or the even stronger MC/DC.

Modified Condition/Decision Coverage (MC/DC)

MC/DC is the gold standard for safety-critical systems (avionics standard DO-178C, automotive AUTOSAR, medical device IEC 62304). It extends condition coverage with one additional requirement: each condition must independently affect the outcome of the decision.

For each atomic condition C, there must be a pair of test cases that differ only in the value of C, while all other conditions remain constant, and the overall decision outcome changes. This proves that C is not redundant and that a fault in C would be observable in the decision output.

MC/DC for a decision with N conditions requires at minimum N+1 test cases (compared to 2² for full combinatorial coverage). For a decision with 5 conditions, MC/DC needs at least 6 test cases, not 32.

Worked example: insurance eligibility

A function determines insurance eligibility with the condition:

if (age >= 18 && hasLicence && noClaims) {
  eligible = true;
} else {
  eligible = false;
}

Three atomic conditions: A = age ≥ 18, B = hasLicence, C = noClaims.

Branch coverage minimum: 2 tests (overall true, overall false). Condition coverage minimum: 3 pairs of tests, one for each condition evaluated both ways.

Insurance eligibility — condition coverage test cases
TC A: age ≥ 18 B: hasLicence C: noClaims Eligible Conditions exercised
TC1T (age=25)TTtrue A=T, B=T, C=T
TC2F (age=16)TTfalse A=F — covers A both ways with TC1
TC3T (age=25)FTfalse B=F — covers B both ways with TC1
TC4T (age=25)TFfalse C=F — covers C both ways with TC1
MC/DC pairs — each condition independently determines outcome
Condition under test Test case 1 Test case 2 A B C Result changes?
A independently affects outcome TC1TC2 T→FT (same)T (same) Yes (true→false)
B independently affects outcome TC1TC3 T (same)T→FT (same) Yes (true→false)
C independently affects outcome TC1TC4 T (same)T (same)T→F Yes (true→false)

TC1 through TC4 achieve both condition coverage and MC/DC for this three-condition decision. Four test cases cover what branch coverage would achieve with two tests — but each of TC2, TC3, and TC4 targets a specific condition in isolation that branch coverage leaves unverified.

Deriving condition coverage test cases systematically

  1. Identify atomic conditions — decompose every compound boolean decision in the code into its atomic sub-expressions. Each sub-expression that cannot be split further is one condition to cover.
  2. For each condition, find a baseline — a test case where all other conditions are true (for AND logic) so the condition under test independently determines the outcome. This is your MC/DC anchor test.
  3. Create a partner — copy the baseline test and flip only the condition under test. Verify the overall decision outcome flips. This is the MC/DC pair.
  4. Verify coverage — check that every atomic condition appears as true in at least one test and false in at least one test across the complete test suite.
  5. Add branch coverage check — confirm the overall decision is true in at least one test and false in at least one test. Combined condition/decision coverage is now achieved.

ISTQB mapping

ISTQB reference
Syllabus refTopicLevel
CTFL 4.3.3Condition coverage — basic definition and distinction from branch coverageFoundation
CTAL-TA 4.3Condition coverage, condition/decision coverage, and MC/DC — full applicationAdvanced / Senior
CTAL-TA 4.3 K4Analyse code to determine test cases achieving condition coverage and MC/DCAdvanced LO

Foundation candidates need to know that condition coverage exists and how it differs from branch coverage. Advanced (CTAL-TA) candidates must be able to derive test cases that achieve MC/DC for a given compound decision — this is a K4 (analyse) objective.

Common mistakes

  • Confusing condition coverage with branch coverage — they are not equivalent. Branch coverage checks the overall decision outcome; condition coverage checks each atomic sub-expression. A test suite can achieve 100% branch coverage while leaving conditions untested in one direction.
  • Forgetting short-circuit evaluation — in most languages, A && B does not evaluate B if A is false. A test where A=false may not exercise B at all. Design tests specifically to ensure B is evaluated (set A=true) when testing B.
  • Not testing each condition independently — basic condition coverage only requires each condition to be true and false somewhere. MC/DC additionally requires independence. Write the independence argument explicitly for safety-critical work.
  • Applying condition coverage to trivial single-condition decisions — if a decision has only one atomic condition (e.g., if (isLoggedIn)), condition coverage is identical to branch coverage. Only invest in the distinction when decisions are compound.
  • Assuming 100% condition coverage means no bugs — condition coverage verifies that each condition is exercised both ways. It does not verify that the logical operator between conditions is correct. A developer who wrote || instead of && may not be caught by condition coverage alone.

4 Industry Reality

🏭 What you actually encounter on the job
  • Coverage tools report percentages, not quality. Most CI pipelines show a single "coverage %" from statement or branch coverage. Condition coverage and MC/DC are rarely tracked automatically — you have to argue for them, configure the tool explicitly (Istanbul, JaCoCo, gcov), and educate stakeholders that 80% branch coverage is not the same thing as 80% condition coverage on compound decisions.
  • Legacy NZ financial and insurance code is littered with compound conditions. Eligibility rules for KiwiSaver, HomeStart grants, or CoverNZ entitlements typically end up as multi-condition if-statements built over years by different developers. Nobody drew up an MC/DC table. The defects hide in the conditions that were never tested false — and they surface at audit time or after a complaint to the Banking Ombudsman.
  • Time pressure means you prioritise. No project has time to apply MC/DC to every decision. Senior testers focus condition coverage on decisions that control money, access, safety, or compliance — the ones where a wrong outcome has a real consequence. The login-page null check gets branch coverage; the dosing-guard decision gets MC/DC.
  • Short-circuit evaluation is a constant trap in JavaScript and Java code. Developers know their language short-circuits, so they order conditions intentionally (cheapest first, most-likely-false first). As a tester you have to think about evaluation order too — a test that never reaches condition B is not covering B. This only shows up in dynamic analysis, not in reading the code.
  • The "independence" argument rarely gets written down. Teams that claim MC/DC compliance for safety-critical work (medical devices, avionics components) often have the test cases but not the independence argument — the explicit pairing that shows each condition flips the outcome. Auditors and certification bodies (for IEC 62304 or DO-178C) ask for the table. Not having it is a finding. Senior testers produce the documentation, not just the tests.

5 When to Use It — and When Not To

⚡ Decision guide

✓ Use it when

  • The decision under test has two or more atomic conditions joined by && or || — condition coverage adds real value here that branch coverage misses
  • The code controls financial eligibility, access control, dosing, or safety interlocks — any domain where a wrong branch is a regulatory or liability problem
  • You are targeting ISTQB CTAL-TA, IEC 62304, DO-178C, or ISO 26262 compliance — MC/DC is mandated or strongly expected
  • A defect report has already come back from a compound condition and management wants proof the fix is complete and other conditions are not the same risk
  • Code review found a likely operator error (|| vs &&, negation) and you want tests that would definitively catch that class of bug

✗ Skip it when

  • The decision has only one atomic condition — condition coverage is identical to branch coverage and the distinction is meaningless overhead
  • The code is UI glue, configuration loading, or pure-rendering logic with no business rules — branch coverage is sufficient and cheaper
  • Time is genuinely short and the compound decision is in a low-risk, well-established module with stable history — apply the effort to riskier areas instead
  • The conditions are generated or composed dynamically (e.g. a rules engine or expression evaluator) — condition coverage of the engine itself matters more than of each generated expression
  • You are doing exploratory testing or early smoke testing — structural coverage techniques belong in the formal test design phase, not during rapid triage

Context guide

How the right level of condition coverage effort changes based on project context.

Context Priority Why
CoverNZ claim eligibility engine — compound guards controlling injury payment entitlements Essential A wrongly evaluated condition silently denies or over-pays entitlements. MC/DC is the minimum standard; any audit under the Accident Compensation Act 2001 will ask for the independence argument.
Revenue NZ income tax assessment logic — multi-condition eligibility for tax credits and thresholds Essential Compound conditions in tax rules (residency AND income threshold AND filing status) carry direct financial and legal liability. Condition/decision coverage is table stakes; MC/DC is expected if the rule is used in automated assessments at scale.
Pacific Bank or Harbour Bank home-loan affordability calculator — compound guard on lending decision High Reserve Bank responsible-lending obligations and Banking Ombudsman complaints create real liability if a lending gate is miscoded. Condition coverage on the affordability expression catches operator-substitution faults before they trigger a regulatory finding.
TransitNZ (TransitNZ) vehicle warrant-of-fitness pass/fail decision — compound safety-check conditions High A WoF system that incorrectly passes an unsafe vehicle creates safety risk and regulatory exposure. Each safety-check condition must be independently verified false so no single fault can mask an unsafe reading.
Pacific Air seat-upgrade eligibility — compound loyalty tier and fare-class condition Medium A miscoded upgrade rule causes commercial and customer-service cost but not regulatory breach. Branch coverage is usually sufficient; add condition coverage if a defect has already been reported or the rule is frequently changed by commercial teams.
TeleNZ marketing-banner display condition — compound device-type and campaign-flag condition Low The worst outcome is a misplaced banner — cosmetic and caught in exploratory testing. Branch coverage is the right investment; reserve condition coverage effort for the billing and eligibility logic behind the same page.

Trade-offs

What you gain and what you give up when you choose Condition Coverage.

Advantage Disadvantage Use instead when…
Catches operator-substitution faults (e.g. && written as ||) that branch coverage cannot detect, because each atomic condition must be evaluated false at least once in isolation. Does not subsume branch coverage. A test suite can reach 100% condition coverage while never taking both branches of the overall decision — you must combine it with decision coverage to close that gap. The decision has a single atomic condition — use branch coverage, which is identical in that case and adds no overhead.
Scales to N+1 tests (MC/DC) for N conditions — far cheaper than the 2N combinations required by full combinatorial coverage, yet still provides a documented independence argument for auditors. Short-circuit evaluation in languages like JavaScript and Java can silently prevent conditions from being reached at runtime, making tool-reported percentages misleading. Dynamic analysis traces are required to confirm actual evaluation. The codebase uses a rules engine or expression evaluator that generates conditions dynamically — apply condition coverage to the engine logic itself rather than to each generated expression.
Directly supports regulatory compliance in NZ contexts (IEC 62304 for medical devices, DO-178C for avionics components) and provides the independence argument table that auditors and certification bodies require. Adding a fourth condition to an existing compound decision invalidates any previously approved MC/DC set — the entire independence table must be re-derived from scratch, which is easy to miss in fast-moving sprints. The decision is in UI glue, configuration loading, or cosmetic display logic with no financial, safety, or compliance consequence — use branch coverage and invest saved effort in higher-risk areas.
Works as a fault-model argument: each atomic condition maps to a specific class of fault (wrong operator, inverted boolean). Documenting which condition each test targets makes defect attribution and regression test selection straightforward. Even at 100% condition coverage, a wrong logical operator between conditions may go undetected unless MC/DC independence pairs are used. Basic condition coverage without the independence requirement is not sufficient to catch all operator-mutation faults. The team already has mutation testing running in CI that kills boolean operator mutations — pair mutation testing with condition coverage rather than treating them as alternatives; mutation testing can only kill mutants your tests actually reach.

Enterprise reality

How condition coverage changes when you have 200-plus developers, mandatory compliance obligations, and test gates enforced at the pipeline level — not just on someone's checklist.

  • At scale, condition coverage gates are automated in CI rather than manually reviewed. Teams at Pacific Bank and Revenue NZ embed JaCoCo or Istanbul in condition/decision mode as a quality gate — builds that drop below the agreed threshold on financial-eligibility modules are blocked from merging, not flagged for someone to look at later.
  • Governance and audit obligations make the independence argument non-negotiable. Under the Privacy Act 2020, NZISM (version 3.7), PCI DSS, and the Health Information Security Framework (HISF), access-control and eligibility logic must be independently verified — auditors ask for the MC/DC table, not a coverage percentage. Organisations without it receive findings; those with it pass in one review cycle.
  • Tooling decisions at volume shift toward purpose-built analysis. Small teams use Istanbul or JaCoCo configured in condition mode. At 10-plus squads, organisations invest in Parasoft, LDRA, or VectorCAST because they generate the MC/DC independence tables automatically and produce audit-ready reports — the manual effort of deriving pairs for hundreds of compound conditions becomes unsustainable without tooling that does it for you.
  • Cross-squad coordination requires a shared condition-coverage policy owned by the QA chapter or Centre of Excellence, not individual squads. Without a central policy, squads apply inconsistent standards — one team does MC/DC on every compound condition, another does branch coverage and calls it done. At 10-plus squad scale, the QA chapter defines which risk tiers require MC/DC, which require condition/decision coverage, and which can use branch coverage, then enforces it through shared pipeline templates so the standard is consistent regardless of which squad owns the code.

What I would do

Professional judgment — when to reach for condition coverage, when to skip it, and what to watch for.

If…
I am reviewing a story for the Benefits NZ Jobseeker Support eligibility service, and the acceptance criteria include a compound condition gating benefit approval — for example, if (incomeBelow && residencyConfirmed && activelyJobSeeking)
I would…
Reject the story without an MC/DC table attached to the test plan. Benefit approval decisions are Privacy Act 2020 and Social Security Act 2018 territory — a wrong operator or inverted condition is a complaints-tribunal finding waiting to happen. I would write the four-test MC/DC set myself (baseline all-true, one partner per condition) during planning, attach the independence argument, and make it a definition-of-done item so the developer's unit tests also target the same pairs.
If…
I am testing a HealthNZ patient-record access-control gate in a clinical system — the condition is something like if (userIsClinicianForPatient && recordNotRestricted && withinConsentScope) — and the team says condition coverage is "covered" because Istanbul reports 87%
I would…
Pull the dynamic analysis trace and verify that all three conditions were actually evaluated at runtime, not just seen in source. Short-circuit evaluation in JavaScript means Istanbul can report a line as covered while the runtime skipped the second and third conditions entirely. I would open the test data, confirm each condition was exercised false in at least one test, and flag under the Health Information Privacy Code 2020 that an unauthenticated access path is a notifiable privacy breach — making this a risk the product owner must sign off on, not a tester judgment call.
If…
I am working on an FamiliesNZ case-management system and a developer adds a fourth condition to an existing three-condition compound guard that already has an approved MC/DC set
I would…
Treat the code change as a full test-design change request. Adding a condition invalidates the previous MC/DC coverage claim because the new condition has no independence pair, and the existing pairs were not designed to hold it constant. I would re-derive the MC/DC set from scratch (N+1 = 5 tests for four conditions), update the independence argument table, and raise a risk note: any prior compliance record citing the old test set as evidence of MC/DC coverage must be updated or it becomes a false assurance artefact.

The bottom line: Condition coverage is not a metric to report — it is a fault-model argument. Every atomic condition is a place a developer can write the wrong operator. Your job is to prove, test by test, that each of those fault sites was exercised both ways. For financial entitlements, safety interlocks, and access-control gates in NZ systems, that argument must be explicit and documented, not inferred from a percentage.

6 Best Practices

✓ What experienced testers do
  • ✓ Start with condition identification, not test cases. Before writing a single test, list every atomic condition in the decision. Number them. This forces you to see B, C, D as separate coverage targets, not just background values while you vary A.
  • ✓ Account for short-circuit evaluation before designing tests. Determine the evaluation order and draw a truth table with "N/A (not evaluated)" where short-circuit prevents a condition from being reached. If N/A appears in a condition column, you need a dedicated test that reaches it.
  • ✓ Build the baseline test first (all conditions true for AND, all false for OR). The baseline is the anchor for your MC/DC pairs. Every partner test is just the baseline with one condition flipped — this keeps your test set minimal and the independence argument obvious.
  • ✓ Write the independence argument explicitly, not just the test cases. For each condition, note which pair of tests proves independence and state the outcome change. A coverage percentage without this argument does not satisfy safety-critical auditors.
  • ✓ Combine condition coverage with decision coverage (use condition/decision coverage). Pure condition coverage does not subsume branch coverage. Always verify that the overall decision also goes true and false — add this check as a column in your coverage table.
  • ✓ Label which conditions each test case exercises in its name or notes. Test names like "TC_B_false_isolated" make coverage analysis at review time instant. Cryptic test names force reviewers to reverse-engineer what is being covered.
  • ✓ Flag compound decisions in code review as test-design triggers. When you review a PR containing a new compound condition, open a test-design ticket. The developer's unit tests rarely achieve condition coverage — they tend to test the happy path and one failure path.
  • ✓ Use a coverage tool to confirm, but do not let the tool define your strategy. Run Istanbul, JaCoCo, or gcov in condition/branch mode to verify you have not missed anything — but design tests from the requirement first, then confirm coverage second. Tool-first design tends to produce tests that satisfy the metric but miss real faults.
  • ✓ Document the minimum test count and why. For a decision with N conditions, MC/DC needs N+1. When stakeholders ask why you have four tests for three conditions instead of two, the N+1 rationale is your answer. Attach it to the test plan.
  • ✓ Re-derive condition coverage tests when conditions are added or changed. A developer who adds a fourth condition to an existing compound decision invalidates the previous MC/DC set — the new condition needs a baseline-partner pair, and existing pairs may need new baselines. Treat condition changes as test-design change requests.

7 Common Misconceptions

❌ Myth: "We have 100% branch coverage, so condition coverage must be covered too."

Reality: Branch coverage and condition coverage are independent criteria. A compound condition like A && B can achieve 100% branch coverage (overall decision both true and false) with only two tests — but in those two tests, B might only ever be evaluated true. The condition B=false is never exercised. Condition coverage is not a subset of branch coverage; you need to explicitly design tests for each atomic condition. Teams that assume branch coverage implies condition coverage are leaving whole categories of operator-substitution faults (wrong boolean operator, inverted negation) undetected.

❌ Myth: "Condition coverage guarantees I'll catch a wrong operator — like && written as ||."

Reality: Basic condition coverage requires each condition to be true and false, but it does not require each condition to independently flip the overall outcome. You can satisfy condition coverage with a test set where both conditions happen to be false at the same time — so the overall result is false regardless of which operator connects them. MC/DC is the criterion that catches operator mutations: for each condition, there must be a pair of tests that differ only in that condition and flip the outcome. Without the independence requirement, a wrong operator may remain undetected even at 100% condition coverage.

❌ Myth: "MC/DC is only for avionics and medical devices — it's overkill for regular software."

Reality: MC/DC originated in DO-178C avionics certification, but the underlying problem it solves — proving each condition independently controls the outcome — is relevant anywhere the cost of a wrong branch is high. NZ examples: a KiwiSaver withdrawal eligibility rule, an CoverNZ claim threshold decision, or a building-consent eligibility check are all worth MC/DC treatment. The technique is also surprisingly cheap: N+1 tests, not 2N. The stigma of "only for safety-critical" causes teams to under-invest in condition testing on business-critical rules that carry financial and legal exposure just as real as an avionics bug.

8 Now You Try

Three graded exercises — spot, fix, then build. Write your answer, run it for AI feedback, then compare to the model answer.

🔍 Exercise 1 of 3 — Spot: which condition is half-tested

A KiwiSaver first-home grant uses if (yearsContributing >= 3 && isFirstHome). Two tests run: (a) yearsContributing=5, isFirstHome=true → eligible; (b) yearsContributing=1, isFirstHome=true → not eligible. State which atomic conditions are evaluated true and false across these tests, and name the condition that is never tested both ways.

Show model answer
Condition A (yearsContributing >= 3): test (a) makes A=true (5 >= 3), test (b) makes A=false (1 >= 3 is false). A is covered both ways. ✓
Condition B (isFirstHome): both tests use isFirstHome=true, so B is only ever evaluated TRUE. B=false is never tested. ✗

Condition never tested both ways: B (isFirstHome).

Why it matters: a defect that fires only when isFirstHome is false — for example, wrongly granting the first-home grant to a repeat buyer who has contributed long enough — would never be triggered. Condition coverage requires each atomic condition to be evaluated both true and false; a third test with isFirstHome=false is needed.
🔧 Exercise 2 of 3 — Fix: repair a "condition coverage" claim

A tester claims the two tests below give full condition coverage for if (isResident && age >= 18) on an electoral-roll enrolment check. The claim is wrong. Rewrite the set so each atomic condition is evaluated both true and false, and add the branch-coverage check.

Flawed set:
TC1: isResident=true, age=30 → enrol
TC2: isResident=true, age=15 → reject
"Both conditions covered — full condition coverage."

Rewrite for full condition coverage:

Show model answer
Correct condition-coverage set for (isResident && age >= 18):
- TC1: isResident=true, age=30 → A=true, B=true → overall TRUE (enrol)
- TC2: isResident=false, age=30 → A=false, B=true → overall FALSE
- TC3: isResident=true, age=15 → A=true, B=false → overall FALSE

Now isResident is both true (TC1, TC3) and false (TC2); age >= 18 is both true (TC1, TC2) and false (TC3). Each atomic condition is evaluated both ways. ✓

What was wrong with the original:
- isResident was true in BOTH tests — its false value was never evaluated, so condition coverage was NOT achieved.
- Only age changed between the two tests; isResident was never exercised as false.
- The two tests do happen to give branch coverage (overall true and overall false), which is probably why it looked complete — but branch coverage ≠ condition coverage. Add the branch check explicitly: overall TRUE in TC1, overall FALSE in TC2/TC3 — both branches covered too.
🏗️ Exercise 3 of 3 — Build: an MC/DC set for three conditions

A medical-device dosing guard for a NZ hospital uses if (A && B && C) where A = weightOk, B = ageOk, C = noAllergy. Design an MC/DC test set: show, for each condition, a pair of tests that differ only in that condition and flip the overall outcome. State the minimum number of tests.

Show model answer
MC/DC for (A && B && C), minimum N+1 = 4 tests:
- TC1 (baseline): A=T, B=T, C=T → overall TRUE
- TC2: A=F, B=T, C=T → overall FALSE   (pairs with TC1 to prove A independently flips the outcome — only A changed, result changed)
- TC3: A=T, B=F, C=T → overall FALSE   (pairs with TC1 to prove B independently flips the outcome)
- TC4: A=T, B=T, C=F → overall FALSE   (pairs with TC1 to prove C independently flips the outcome)

For an AND of N conditions, the baseline has all conditions true; each partner flips exactly one condition false, holding the others true, so the single changed condition is the sole cause of the outcome changing from true to false. That is the independence MC/DC demands. Four tests, not 2³=8 — MC/DC is far cheaper than full combinatorial coverage while still proving each condition matters.

Why teams fail here

  • Trusting a branch-coverage percentage as a proxy for condition coverage — the tool shows green, the compound condition is half-tested, the bug ships.
  • Forgetting short-circuit evaluation: a test where the first condition is false never reaches later conditions at all, so those conditions appear "covered" in the source but were never actually evaluated.
  • Writing MC/DC test cases but not documenting the independence argument — auditors and certification bodies (IEC 62304, DO-178C) require the explicit pairing table, not just test results that happen to satisfy the numbers.
  • Applying condition coverage to new code but not re-deriving it when a developer later adds a fourth condition — the existing MC/DC set no longer covers the new condition and the coverage claim becomes silently invalid.

Key takeaway

Branch coverage tells you the decision was reached both ways; condition coverage tells you every factor inside that decision was actually exercised — and for financial, safety, or compliance logic in NZ software, you need both.

How this has changed

The field moved. Here is how Condition Coverage evolved from its origins to current practice.

1970s

Condition coverage defined alongside branch coverage in structural testing theory. Where branch coverage tests whether each decision outcome is reached, condition coverage tests whether each individual Boolean sub-expression evaluates to both true and false independently.

1980s–90s

Modified Condition/Decision Coverage (MC/DC) defined and adopted by avionics regulators. DO-178B (and later DO-178C) requires MC/DC for the highest safety integrity levels in flight-critical software. MC/DC becomes the gold standard for safety-critical structural testing.

2000s

Code coverage tools add condition coverage measurement. Most commercial software development never uses it — branch coverage is considered sufficient. MC/DC remains specific to aerospace, rail, automotive, and medical device software.

2010s

IEC 61508 and ISO 26262 (automotive) reference MC/DC-equivalent coverage for their highest integrity levels. Fuzz testing challenges the assumption that structural coverage alone indicates adequate testing quality.

Now

Condition coverage remains primarily a regulated-industry concern. AI tools can generate condition-exercising tests automatically from code analysis — making MC/DC achievable without manual derivation. Mutation testing provides an alternative measure of test thoroughness for teams not subject to regulatory coverage requirements.

Self-Check

Click each question to reveal the answer.

Interview Questions

What NZ hiring managers ask about Condition Coverage — and what strong answers look like.

What is Modified Condition/Decision Coverage (MC/DC) and why do aviation standards require it?

Strong answer: MC/DC requires that each Boolean condition in a decision independently affects the decision outcome. For a decision A && B && C, branch coverage requires reaching both true and false outcomes. MC/DC requires test cases showing that flipping A alone (while B and C are fixed) changes the outcome, flipping B alone changes the outcome, and flipping C alone changes the outcome. DO-178C requires MC/DC for flight-critical software because it ensures every condition's logic is independently verified — a single fault in one condition cannot be masked by another condition always dominating.

Senior/Lead

How does MC/DC coverage differ from full combinatorial coverage, and what is the trade-off?

Strong answer: Full combinatorial coverage tests all 2^N combinations of N Boolean conditions. MC/DC requires only N+1 test cases (one for each condition, plus one baseline). For a 5-condition decision, full combinatorial requires 32 tests; MC/DC requires 6. The trade-off: MC/DC cannot detect faults that only manifest when two specific conditions are simultaneously true — but it is tractable. Full combinatorial is theoretically complete but grows exponentially. MC/DC is a practical standard that provides strong but not exhaustive coverage, which is why regulators accept it for all but the most extreme safety integrity levels.

Mid/Senior

Q1: What does condition coverage require that branch coverage does not?

Condition coverage requires each atomic boolean sub-expression inside a compound decision to be evaluated both true and false at least once. Branch coverage only requires the overall decision to go true and false — which a compound condition can do while one of its atomic conditions is never exercised both ways.

Q2: Does condition coverage subsume branch coverage?

No. It is possible to achieve 100% condition coverage without taking both branches of the overall decision. That is why teams require condition/decision coverage (both criteria combined) or the stronger MC/DC, rather than condition coverage alone.

Q3: How does short-circuit evaluation complicate condition coverage?

In most languages, A && B does not evaluate B when A is false. A test with A=false may never run B at all, so B is not exercised. To exercise B you must set A=true and vary B — design tests deliberately so the condition under test is actually reached and evaluated.

Q4: What extra requirement does MC/DC add on top of basic condition coverage?

Independence: each condition must be shown to independently affect the outcome. For every condition there must be a pair of tests that differ only in that condition while all others are held constant, and the overall decision outcome changes. This proves the condition is not redundant and that a fault in it would be observable.

Q5: For a decision with five atomic conditions, how many tests does MC/DC need, and how does that compare to full combinatorial coverage?

MC/DC needs at minimum N+1 = 6 tests for five conditions. Full combinatorial coverage would need 2⁵ = 32. MC/DC gives strong, independence-proven coverage at a fraction of the cost, which is why safety-critical standards (DO-178C, IEC 62304) mandate it rather than exhaustive testing.

Q6: Your team is testing the CoverNZ injury claim eligibility check on an Benefits NZ portal. The condition is if (injuryRecorded && treatmentReceived && claimWithin12Months). The developer's unit tests already achieve 100% branch coverage. How do you explain to the delivery lead why you need additional tests, and which conditions would you prioritise covering?

A: Branch coverage only confirms the overall claim decision reached true and false — it does not verify each of the three atomic conditions was independently exercised. You would explain that a developer who accidentally wrote || for one operator, or inverted a condition, could pass all existing tests undetected. You would prioritise claimWithin12Months first because time-limit conditions are the most commonly miscoded (off-by-one on months, timezone issues) and carry the most financial and compliance risk. An MC/DC set of four tests (baseline all-true plus one partner per condition) replaces this gap with minimal effort and gives you a documented independence argument for any audit.

Q7: What is the key difference between condition coverage and condition/decision coverage, and why do most test standards require the combined form rather than condition coverage alone?

A: Condition coverage only requires each atomic condition to be true and false at some point in the test suite — it says nothing about whether the overall decision outcome changes. Condition/decision coverage adds the branch coverage requirement: the overall decision must also go both true and false. The combined form is required because condition coverage alone can be satisfied by a test set that never actually takes both branches — for example, tests where some conditions cancel each other out so the overall result is always false. ISTQB CTAL-TA and most safety standards mandate condition/decision coverage (or the even stronger MC/DC) precisely to close this loophole and ensure structural coverage at both the individual-condition and whole-decision level.

Q8: A developer reviewing your test plan says: "We already have mutation testing running in CI — it kills operator mutations like && vs ||, so we don't need to bother with condition coverage." What is wrong with this reasoning and how do you respond?

A: Mutation testing can only kill a mutant if an existing test actually evaluates the affected condition in the right state to observe the difference. If your test suite never evaluates a condition as false (because branch coverage let you get away with it), a mutation on that condition will survive — mutation testing and condition coverage are complementary, not substitutes. The developer's claim assumes the test suite already exercises every condition both ways, which is exactly what condition coverage is designed to guarantee. The correct answer is to achieve condition (or MC/DC) coverage first so every condition is exercised both ways, and then run mutation testing on top to verify the assertions are strong enough to catch the mutations that condition coverage made visible.

Q9: When should you skip condition coverage even for a compound decision, and can you give a realistic NZ government system example where skipping it would be the right call?

A: Skip condition coverage when the compound decision is in low-risk, non-business-critical code where a wrong branch has no financial, safety, or compliance consequence — for example, a display-only condition that decides whether to show a decorative banner on the RealMe login page (if (isMobile && showBanner)). If the banner appears incorrectly, the impact is cosmetic and caught in exploratory testing. Condition coverage effort is better spent on the authentication eligibility check or the identity-verification gate that sits behind that same page. The rule of thumb: if a wrong branch triggers a financial payment, denies an entitlement, or creates a compliance breach (Revenue NZ, CoverNZ, TransitNZ), apply condition/decision coverage or MC/DC. If the worst outcome is a visual glitch, branch coverage is sufficient.

Condition coverage sits above Branch Coverage in strength: branch coverage is a prerequisite, and condition coverage adds the per-atomic-condition requirement on top.

For the complete white-box coverage hierarchy: Statement Coverage ⊂ Branch Coverage ⊂ Condition Coverage ⊂ MC/DC ⊂ Path Coverage. Each level subsumes the one below it, but adds test cases that the lower level misses.

The conditions you are testing with condition coverage often correspond directly to the rows of a Decision Table. Combining both approaches — decision tables to enumerate the specification logic, condition coverage to verify the implementation — gives strong coverage from both ends.

Practice this technique: Try Test Lead Practice 07 — Test coverage gaps.