skip to content

Test Design Techniques

Systematic ways to choose which cases to write, drawing from the specification and the code's structure rather than intuition. Interviewers ask because 'I test the happy path and a couple of edge cases' is not a method.

on this pageshow

questions

20

In boundary value analysis, which values do you test for a field that accepts 1 through 60?

level: juniorimportance: must knowfreq 82%

answer

  1. Faults gather where a rule flips
  2. Test the edge, not the middle
  3. One value on, one just past
  4. Limits come from the specification
  5. Empty and zero are edges too

basics

~20 s

Test the edges, not the middle: 0 and 1 low, 60 and 61 high. Defects hide where a comparison flips from accept to reject, so each limit gets a value on it and one just past.

solid answer

~50 s

Boundary value analysis says faults gather at the edges of an allowed range, because that is exactly where a comparison flips from accept to reject. For a field accepting 1 through 60 the two-value form gives four cases: 0 and 1 at the lower edge, 60 and 61 at the upper edge — one value sitting on each stated limit and one immediately outside it. The three-value form adds 2 and 59, the values immediately inside. The limits must be read from the specification, not from a constant found in the code, or the tests inherit whatever edge the code already believes in. A mid-range value such as 30 is still worth one case as a sanity check, but adding more of them buys almost nothing. Empty, missing and non-numeric entries are separate edges and need their own cases.

code

pseudocode · 9 lines
pseudocode
SPEC: weekly hours accepted when 1 <= hours <= 60

two_value   = [0, 1, 60, 61]
three_value = [0, 1, 2, 59, 60, 61]

expect reject(0)
expect accept(1)
expect accept(60)
expect reject(61)

go deeper

for a junior

Be ready to name the four values for a stated range on the spot and say which two you expect to be accepted. Interviewers use this as a quick check that you can turn a written rule into concrete cases.

for a middle

Explain what each case proves — the on-limit value pins the constant, the outside value pins the direction of the comparison — and extend the idea to length, collection size and period edges rather than only numeric ranges.

for a senior

Show that you also apply it to values the system computes internally, not just to form fields, and that every edge case carries a real statement of expected behaviour rather than a check that nothing blew up.

for a principal

Own the tradeoff that boundary cases multiply with every limit in the rule set. Be ready to say how your teams keep the edge cases that carry consequence and stop the suite filling with edges nobody would be hurt by.

## The observation the technique rests on Boundary value analysis is built on a single empirical claim: programs go wrong at the edges of the ranges they accept, far more often than they go wrong in the middle. The reason is mechanical rather than mystical. Somewhere in the implementation a rule is expressed as a comparison, and a comparison has exactly one place where its answer changes. Everything on one side behaves identically, everything on the other side behaves identically, and the interesting event is the single step across. If the comparison uses the wrong relational operator, or the wrong constant, the mistake is visible only within one step of that edge. A value in the middle of the range cannot expose it. So the technique is a rule for spending cases where they buy something. Once you have decided that a set of values is meant to be treated the same way, you stop sampling that set randomly and start sampling it at its ends. ## Locating the boundary The boundary is a property of the specification, not of the source. For a field specified as accepting whole numbers 1 through 60, the specification names two limits, and each limit has two sides. The two-value form takes the limit itself and the nearest value outside it: 0, 1, 60, 61. The three-value form adds the nearest value inside each limit — 2 and 59 — so each boundary is probed with a below/on/above triple. A worked example makes the shape concrete. A payroll engine accepts a weekly hours entry specified as 1 through 60 hours, and rejects anything else with a validation message. The cases are: 0 hours must be rejected, 1 hour must be accepted, 60 hours must be accepted, 61 hours must be rejected. Two of those cases assert acceptance and two assert rejection, and the pair straddling each limit is what proves the limit sits where the specification put it. If the engine were written with a strict comparison at the top end, only the 60-hour case would fail; a 45-hour case would pass happily and tell you nothing. ## What each case is actually asserting The on-boundary case pins the *value* of the limit. The just-outside case pins the *direction* of the comparison. You need both, and you need a real oracle for each — a statement of what the correct behaviour is. Asserting merely that the system did not crash at 61 hours is not a boundary test; asserting that 61 hours is refused with the stated validation outcome is. ## Boundaries that are not numbers Numeric ranges are the teaching example, but most real boundaries are not numeric ranges: - **Length.** A text field specified as 3 to 12 characters has boundaries at 2, 3, 12 and 13 characters. The value of the text is irrelevant; its length is the input under test. - **Collection size.** A batch specified to accept up to 500 employee records has boundaries at an empty batch, a batch of 1, a batch of 500 and a batch of 501. Zero and one are almost always the most productive pair, because loop and aggregation code is frequently written assuming at least one element. - **Period edges.** A rule that pays a bonus for a period ending on the fifteenth has boundaries at the fourteenth, the fifteenth and the sixteenth of that period. - **Precision.** Where money or hours are recorded in fixed steps, the value just outside a limit is one step away, not one whole unit away. If hours are recorded in quarter-hour steps, the value just below 40 is 39.75. ## Why it is cheap Boundary cases are the highest-yield cases per unit of effort in most suites, because they are small, fast, deterministic and derived directly from written rules — which also makes them easy to justify to a reviewer and easy to regenerate when the rule changes. Their cost is that they multiply: every limit in every field is a candidate, and a large rule set produces more candidates than any suite can hold. ## The common ways it is done badly Three mistakes account for most of them. The first is testing only the on-boundary value and skipping the value outside, which leaves the direction of the comparison unproven. The second is reading the limit off the implementation, so the test agrees with the bug. The third is stopping at the input fields and never boundary-testing values the system computes for itself — a rounded total, an accumulated count, a derived age — where the same edges exist and nobody wrote them on a form.

  • How would you apply the same technique to a field specified as 3 to 12 characters?
    The input under test is the length, so the cases are entries of 2, 3, 12 and 13 characters, with 4 and 11 added if you want the three-value form. The characters themselves do not matter for these cases; use the simplest content that keeps the length unambiguous. An empty entry is a separate case, because empty is usually governed by a required-field rule rather than by the length rule.
  • Which boundary is most often forgotten on a collection input?
    The empty collection, and the collection of exactly one. Aggregation and loop code is routinely written on the assumption that something is there — a first element to compare against, a divisor that is not zero, a header row to emit. A batch of zero records and a batch of one record cost almost nothing to run and expose that assumption immediately, while a batch of five hundred usually just confirms what a batch of two already proved.
  • Do you still need a mid-range case if you have covered every boundary?
    Usually one, as a cheap sanity check that the ordinary path works at all — a suite made only of edges can pass while the normal case is broken. But one is enough per set of values that is meant to behave identically. Adding more mid-range values is the classic way a suite grows large without getting stronger.

You do not test a fence by standing in the middle of the field. You walk right up to it, put one foot on the line, and then put one foot over.

saying these in an interview costs you the question

  • Only tests the limit value, never the value just outside it
  • Reads the limits out of the code instead of the specification
  • Believes mid-range values are as informative as edge values
  • Thinks boundaries only exist on numeric input fields
  • Skips empty and zero because they look degenerate
  • Asserts only that nothing crashed at the edge

context

open as a page

What is a decision table in test design, and what does each column represent?

level: juniorimportance: must knowfreq 66%

basics

~20 s

A decision table is a grid whose rows list the conditions of a rule set and the actions they produce. Each column is one rule: one combination of condition outcomes plus the actions it triggers. Each surviving rule becomes one test case.

open as a page

What is equivalence partitioning, and how do you pick the value you test from each class?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Equivalence partitioning splits the input domain into classes the software is specified to handle identically, then tests one representative from each valid and each invalid class. If a class really is uniform, one value speaks for all of it.

open as a page

What is pairwise (all-pairs) test design, and why is it so much smaller than the full cross-product?

level: juniorimportance: must knowfreq 68%

basics

~20 s

Pairwise test design picks a small set of configurations in which every pair of values from two different parameters appears together at least once. It is tiny compared with the full cross-product because a single row covers many pairs simultaneously.

open as a page

What are states, events, guards and transitions in a state transition model?

level: juniorimportance: must knowfreq 66%

basics

~20 s

A state is a condition the system rests in between inputs. An event is an incoming trigger. A guard is a condition that must hold for that trigger to fire. A transition is the resulting move plus any action it performs.

open as a page

In state transition testing, what is the difference between 0-switch and 1-switch coverage?

level: middleimportance: must knowfreq 57%

basics

~10 s

0-switch coverage exercises every single valid transition at least once. 1-switch coverage exercises every valid pair of consecutive transitions, so it catches defects that only appear when one move follows another.

open as a page

What is the difference between two-value and three-value boundary value analysis?

level: middleimportance: should knowfreq 56%

basics

~20 s

Two-value analysis takes the limit itself and the nearest value outside it. Three-value adds the nearest value inside, giving a below/on/above triple per limit — about fifty percent more cases, and it catches a limit that has shifted.

open as a page

How do you collapse a full decision table using don't-care entries without losing coverage?

level: middleimportance: should knowfreq 47%

basics

~20 s

Merge columns that fire identical actions and differ in exactly one condition, replacing that condition's cell with a don't-care dash. Repeat until nothing merges. Mark unreachable combinations as infeasible instead of dashing them, and re-expand a dash wherever the risk warrants full combinations.

open as a page

Why should each invalid equivalence class get its own test case rather than one combined case?

level: middleimportance: should knowfreq 58%

basics

~20 s

Because a rejected input usually stops at the first failing check, so a second invalid value in the same case is never reached. Isolating one invalid class per case keeps every rejection path exercised and every failure diagnostic.

open as a page

In pairwise test generation, how do you handle forbidden parameter combinations?

level: middleimportance: should knowfreq 52%

basics

~20 s

Declare forbidden combinations as constraints in the model so the generator never emits them and never counts their pairs as owed. Deleting offending rows afterwards silently destroys coverage of legitimate pairs that shared those rows.

open as a page

What do blank cells in a state transition table mean, and how do you test them?

level: middleimportance: should knowfreq 45%

basics

~20 s

A blank cell is a state-event pair the specification never defined. Test it by driving the system into that state, firing that event, and checking the system rejects or ignores it safely instead of quietly moving somewhere the model never drew.

open as a page

How can an off-by-one defect survive a suite that already contains boundary tests?

level: seniorimportance: should knowfreq 54%

basics

~20 s

Usually because the boundary cases were derived from the implementation rather than the written rule, so they agree with the bug. Ambiguous wording, two rules claiming one edge value, and a weak assertion produce the same green result.

open as a page

How do you audit a decision table for contradictory, duplicated or missing rules?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Compare the covered combination count against the product of the condition outcome counts to find missing rules. Two rules matching the same combination are duplicated if their actions agree and contradictory if they differ. Every finding goes back to the specification owner before any test is written.

open as a page

How do you detect that an equivalence class you assumed was uniform actually hides sub-partitions?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Treat uniformity as a hypothesis and look for evidence against it: unexecuted branches under a one-value-per-class suite, outcomes that vary with retries, caching, size, locale or load, and every escaped defect, which is a report that some class holds two behaviours.

open as a page

When is a pairwise-only configuration suite unsafe, and what do you add to close the gap?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Pairwise is unsafe wherever failure needs three or more values together, depends on operation order, or must be evidenced for specific named configurations. Close the gap by seeding required rows, raising strength on a risk-ranked subset, and asserting real outcomes.

open as a page

How do you decide which boundaries earn a dedicated test case when a rule set has more edges than the budget allows?

level: principalimportance: should knowfreq 44%

basics

~20 s

Inventory the edges from the written rules, then rank by the consequence of getting each one wrong. Expensive edges get dedicated cases; cheap ones go to the fastest level or nowhere. Let escaped defects re-rank the list.

open as a page

How do you choose the interaction strength for a large configuration matrix, and defend the cost?

level: principalimportance: should knowfreq 38%

basics

~20 s

Start at strength 2 everywhere because it is cheap, then raise strength only on a small risk-ranked sub-model where a mechanism makes higher-order interaction plausible. Defend the choice with run-time budget, escaped-defect evidence and stated residual risk.

open as a page

How do you apply equivalence partitioning to the output domain rather than to input values?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

Partition the set of distinct results the software can produce, then work backwards to find an input that lands in each one. It covers outcomes that many different inputs share, and outcomes the input fields do not obviously name.

open as a page

What is the difference between a covering array and an orthogonal array in pairwise test design?

level: middleimportance: nice to knowfreq 20%

basics

~20 s

A covering array requires every pair of values to appear at least once. An orthogonal array additionally requires every such pair to appear the same number of times. Balance costs rows, and testing rarely needs it.

open as a page

How do you contain state explosion in a behaviour model that is too large to test?

level: seniorimportance: nice to knowfreq 22%

basics

~10 s

Stop encoding data in states. Move data conditions into guards, fold related states under a superstate, split independent concerns into separate parallel models, and cover only the risky regions at a higher switch level.

open as a page