Let the Integration Layer Decide How Many Tests You Run
Same-tier discounts looked like interchangeable price options on one bundle. In the billing system each was a separate product, so none of them could be collapsed into a representative case.
"Test one, the rest are the same shape."
It is the most reasonable sentence anyone says while scoping a regression suite. It is also a bet — and the bet is about a layer you are not looking at while you say it.
Where Your Equivalence Classes Come From
Scoping the regression for the new-customer journey, we had a set of service discounts sitting on the same broadband tier. On screen they are price options: same bundle, same configuration panel, same fields, differing by an amount.
Every instinct a trained tester has says these are one equivalence class. Same journey, same code path, same rendering, one variable. Run one, note the others as covered by similarity, spend the saved days on something that is genuinely different.
That instinct is not sloppy. It is the technique working exactly as taught. The problem is that the technique needs a definition of "the same," and by default we take it from wherever we happen to be standing — which is the interface.
In the billing system, each of those discounts is a distinct product. Its own catalog entry, its own mapping, its own price configuration.
Not variants of a product. Separate products that the interface happens to render side by side.
Shared Code Fails Once. Configured Data Fails One at a Time.
This is the distinction that decides how many test cases you need, and it is worth being precise about.
Shared logic has a useful property: when it is broken, it is broken for every input that reaches it. Any one case finds it. That property is what makes sampling legitimate — you are not testing every input, you are testing the one piece of code all inputs pass through, and one probe is enough to establish whether it works.
Per-instance configuration has no such property. Each downstream product carries its own mapping, its own price setup, its own set of values that somebody typed once. A mistake in one of them affects exactly one option and is completely invisible from every other. There is no shared component to probe.
So a mapping error or a price-config error on the third discount survives a suite that ran the first discount beautifully, end to end, with every assertion green. The suite tested the code. The defect was in the data, and the data was never a class — it was a list.
The Rule
Let the integration layer, not the interface, decide your granularity.
Before collapsing a set of options into one representative case, ask a single question: does each option produce a distinct artefact in a downstream system?
If the answer is no — the options differ only in a value that flows through the same record, the same mapping, the same downstream product — then thin away. The equivalence class is real.
If the answer is yes, the class does not exist. You have a list of separate things wearing a shared costume, and the costume is a rendering decision, not an architectural one. Each gets a full end-to-end run of its own, and each gets verified where it actually lives.
Working Out Which One You Have
You do not need to read the integration code to answer this. You need to look one layer past the screen.
- Count downstream records, not interface options. Open the catalog in the system that consumes the order. If four options on your screen correspond to four entries there, you have four things to test — that count is your case count, and it is not negotiable by similarity.
- Ask what is configured per instance, and by whom. Anything a person set up individually — a mapping, a price, an external identifier, an effective date — is a per-instance failure mode. Ask who configured them and whether they were configured in one batch, because a batch of four done by hand is four opportunities, not one.
- Verify at the destination, not at the source. A correct-looking cart proves the interface accepted your input. The assertion that matters is on the downstream subscription and the invoice, because that is where a mapping mistake becomes visible and it is the only place it ever will.
One caution on that last point. If your evidence for a discount is a screenshot of the order summary, you have not tested the discount. You have tested that the screen can display a number you typed.
Why This Gets Thinned Anyway
Because thinning is the part of scoping that gets praised.
Regression scope arrives under time pressure, and a tester who proposes cutting a suite from twelve full journeys to four sounds like someone with judgement. A tester who insists on twelve nearly identical runs sounds like someone who has not thought about risk. The conversation happens in a planning session, in front of people looking at a screen where the options genuinely do look identical, and nobody in the room has the downstream catalog open.
The defect that results also disguises itself well. A price-config error on one discount does not present as a regression failure — it presents weeks later as a billing anomaly on a small number of accounts, and it gets triaged as a data issue rather than a hole in test scope. The connection back to the day the suite was thinned is never made, so the thinning is never re-examined, and the same reasonable sentence gets said again in the next scoping session.
That is the loop worth breaking. Similarity you can see is a property of the interface. The system that breaks is the one underneath it.
In your last regression scope, how many cases did you merge because the options looked alike — and did you check what each one creates downstream?