Pick one input field from something you have worked on and design its tests with equivalence partitioning and boundary values.
Split the input domain into classes the code should treat identically, then test each class once and probe every boundary at the smallest step the field allows. Exhaustive testing is ruled out by combinatorics, which is why a coverage number is a claim about your model rather than about correctness.
What the interviewer is scoring
- Does the candidate partition on behaviour rather than simply splitting valid from invalid
- Whether the boundary values are stepped by the field's real granularity instead of by one whole unit
- That internal and relational boundaries get found, not just the two numbers written in the specification
- Whether invalid inputs are varied one at a time, so that no validation rule masks another
- Can they say what a coverage percentage does and does not license them to claim
Answer
Start from the rules, not from the widget
Take the amount field on a domestic bank transfer. Its rules, gathered from the specification and from asking, are: a minimum of 1.00, a per-transaction maximum of 100000.00, at most two decimal places, no more than the available balance, and anything above 50000.00 routes to a second approver instead of settling immediately.
That last rule is the one people miss, because it is not validation. It never rejects your input and it produces no error message, yet it splits the range of legal amounts into two groups that the system handles completely differently. Partitioning on behaviour rather than on acceptance is the whole technique; if you divide the field only into "valid" and "invalid" you will test a 200.00 transfer and a 90000.00 transfer as though they were the same case, and only one of them touches the approval path.
The partitions this field really has
Working from those rules, the classes are: amounts from 1.00 to 50000.00, which settle straight through; amounts from 50000.01 to 100000.00, which settle only after approval; amounts below the minimum; amounts above the per-transaction cap; amounts with three or more decimal places; amounts exceeding the available balance regardless of size; negative amounts; zero; empty; and non-numeric text. Ten classes, and one representative from each is by hypothesis as informative as every member of that class.
The hypothesis is the part worth saying out loud. Partitioning is a bet that the code treats every value in a class the same way, and the bet is only as good as your knowledge of the implementation. If you learn during the work that amounts are parsed by one routine below a threshold and a different one above it, you have discovered a partition boundary the specification never mentioned, and the test set changes.
Boundaries at the granularity the field actually has
Faults cluster at boundaries because that is where a human wrote a comparison operator and had to choose between > and >=. Three-value analysis takes the boundary itself plus one step either side, and the step must be the smallest the field permits. For a currency field with two decimals that step is 0.01, so the cases are 0.99 / 1.00 / 1.01, 49999.99 / 50000.00 / 50000.01, and 99999.99 / 100000.00 / 100000.01.
Testing 49999 and 50001 instead looks similar and proves far less. Both values sit unambiguously inside their partitions, so a threshold coded as amount > 50000 and one coded as amount >= 50000 pass identically, and the off-by-one you were hunting survives.
The balance rule adds a boundary of a different kind, because it compares the input against state rather than against a constant. The interesting cases are amount equal to the balance, one paisa below it, and one paisa above it, which means the test needs the account seeded to a known figure to the last decimal place. A test written against "whatever balance the shared account has today" cannot express this at all, which is usually the moment a candidate discovers that their test data strategy is the real constraint on their test design.
Why exhaustive testing was never available
Do the arithmetic in front of the interviewer. Counting only well-formed two-decimal amounts up to 999999.99, that single field has 10^8 legal values, and because it is a text input the true input space is every string a keyboard can produce, which is unbounded. Now add the rest of the form: say twenty currencies, four account types, three channels, and a two-state approval flag. The product is 10^8 x 20 x 4 x 3 x 2, roughly 4.8 x 10^10 combinations. At one millisecond per case, executing them once takes about 550 days of continuous running, for one form.
Even that overstates what exhaustion would buy you, because the input space is not the whole space. Defects also live in the order of operations, in timing and concurrency, in state left behind by earlier transactions, and in the interaction between two sessions on the same account. Enumerating inputs exhaustively would still leave all of those untested, so complete testing is not merely expensive, it is not a coherent goal.
What that does to a coverage claim
Coverage is a measurement of traversal. Line and branch coverage tell you which statements executed while your tests ran; they say nothing about whether an assertion checked the result, and nothing about which values entered the code. A suite that never sends 1.00 or 100000.01 can reach full line coverage on that validator, because one representative value walks every line while missing both boundaries.
So a coverage figure is honest only when it names its model. "Full branch coverage on the transfer validator" is a defensible statement. "Ninety per cent covered, so the risk is low" is not, because the ten per cent is chosen by whatever your tests happened to reach rather than by risk, and the ninety per cent includes lines that executed without being verified. State coverage against the model you built: ten partitions, nine boundary triples, and the residual risk you consciously left, such as concurrent transfers on one account.
Change one invalid value at a time
The efficiency instinct is to fold the negative cases together: one test that submits -1.005 on an overdrawn account, hitting the negative, the decimal-places and the balance rules in a single request. It will fail for exactly one reason. Validation short-circuits at the first violation it finds, so the other two rules are never exercised, and if either is missing entirely the test still goes green.
The working rule is asymmetric and worth stating as such. Valid representatives may be combined freely, since a passing path has to satisfy every rule anyway, so one test can carry a valid amount, a valid currency and a valid account type at once. Invalid representatives must be isolated, one per test case, each with everything else valid, because only then does a failure identify a rule. This is the single-fault assumption, and it is also why a form with ten fields needs roughly ten negative tests before it needs any clever combinatorial tooling.
Boundaries belong to the implementation, not to the prose: the thresholds worth stepping over are wherever a comparison was written, including the ones the specification never mentions.
Likely follow-ups
- Two of your partitions turn out to share a code path. Does that change the test set, and how would you find out?
- How would you extend this to a form where three fields constrain each other?
- Where does a decision table or pairwise generation earn its keep over hand-picked partitions?
- The field is a free-text box rather than a number. What are its partitions?
Related questions
- A test that has passed for a year starts failing after a refactor that was supposed to change nothing. Is it a bug or a bad test?mediumAlso on test-design4 min
- Your test still reaches the network even though you patched the client. Talk me through fixture scope, parametrisation, and where a patch has to point.mediumAlso on coverage4 min
- How do you split a story that is too big to fit in a Sprint?mediumSame kind of round: concept3 min
- What is the Definition of Done, who owns it, and what do you do with an item that misses it at the end of a Sprint?mediumSame kind of round: concept3 min
- Where does encapsulation or polymorphism change a design, and how do you decide between composition and inheritance?mediumSame kind of round: concept6 min
- What are the four pillars of OOP, and what is the difference between abstraction and encapsulation?easySame kind of round: concept4 min
- Take this table to third normal form, then tell me when you would deliberately denormalise — and what does ACID guarantee?mediumSame kind of round: concept5 min
- Walk me through Scrum as the Guide defines it: the accountabilities, the events, the artefacts, and what each event is for.easySame kind of round: concept5 min