Decision table testing: the combinations everyone skips
A shop ran a promotion. Members got 10% off. Orders over 100 got another 5% off. A promo code gave 15% off and was not supposed to stack with the member discount.
A customer who was a member, spent 240, and used the code got 30% off. The rule about not stacking existed. Nobody had tested a member who used a promo code on a large order, because the ticket described each discount in its own paragraph.
The bug was never in one rule. It was in the combination.
Short version
- A decision table lists every combination of conditions and the expected result for each.
- Conditions go on top, actions below, one column per combination.
- Three yes/no conditions produce 8 combinations, four produce 16.
- Collapse combinations where a condition makes no difference, using a dash.
- Each remaining column becomes one test case with an expected result written in advance.
What a decision table is
A decision table is a grid. The rows at the top are conditions — the yes/no facts about the situation. The rows at the bottom are actions — what the system should do. Each column is one rule: a full combination of conditions plus the expected outcome.
It is useful whenever an outcome depends on more than one input at the same time. Discounts, pricing, permissions, shipping, eligibility, tax, and approval flows are all like this.
The value is not the grid itself. The value is that the grid forces you to write down combinations you would never think of on your own.
Building a decision table, step by step
Use the discount example from the opening.
Step 1: list the conditions.
- Customer is a member
- Cart total is 100 or more
- A valid promo code was entered
Step 2: list the actions.
- Apply 10% member discount
- Apply 5% bulk discount
- Apply 15% promo discount
Step 3: count the combinations. Three yes/no conditions give 2 x 2 x 2 = 8. Write 8 columns. Fill the first condition row with Y Y Y Y N N N N, the second with Y Y N N Y Y N N, the third alternating Y N Y N Y N Y N. That pattern guarantees you miss nothing.
Step 4: fill in the actions, one column at a time, from the written rules.
Step 5: work out the total for each column so the expected result is a single number you can check on screen.
| R1 | R2 | R3 | R4 | R5 | R6 | R7 | R8 | |
|---|---|---|---|---|---|---|---|---|
| Is a member | Y | Y | Y | Y | N | N | N | N |
| Total is 100+ | Y | Y | N | N | Y | Y | N | N |
| Valid promo code | Y | N | Y | N | Y | N | Y | N |
| Member 10% | N | Y | N | Y | N | N | N | N |
| Bulk 5% | Y | Y | N | N | Y | Y | N | N |
| Promo 15% | Y | N | Y | N | Y | N | Y | N |
| Total discount | 20% | 15% | 15% | 10% | 20% | 5% | 15% | 0% |
R1 is the column from the opening story. It exists on the grid whether or not anyone thought of it.
Collapsing the rules you do not need
Look at R1 and R5. The only difference is membership, and the result is the same 20% either way. Once a promo code applies, membership stops mattering. The same is true of R3 and R7.
Replace the condition with a dash, which means "does not matter":
- R1 + R5 becomes: member
—, total 100+Y, promoY→ 20% - R3 + R7 becomes: member
—, total 100+N, promoY→ 15%
Eight rules become six. On a table with four or five conditions, collapsing often removes half the columns.
Two warnings. First, collapse only after you have filled the table in full, never before — collapsing early is how combinations get skipped. Second, a dash is a claim about behaviour, so confirm it with the developer rather than assuming it.
Reading the table for design bugs
A finished table tells you things before you run a single test.
Missing rules. If a column has no action at all, ask what the system should do. R8 gives 0% discount, which is correct. But an empty column in a permissions table usually means an unhandled case.
Contradictions. If two rules with the same conditions produce different actions, the specification disagrees with itself. Raise it now, not after the release.
Impossible combinations. Some columns cannot happen in reality, for example "user is logged out" and "user is an admin". Mark them as impossible and give the reason, so the next reader does not re-open the question.
Example two: a permission matrix
Permissions are the most common place teams get burned, because the interesting cases are combinations of role and ownership.
Conditions: the user's role, whether the user owns the document, and whether the document is locked.
| Role | Owns it | Locked | Can view | Can edit | Can delete |
|---|---|---|---|---|---|
| Viewer | — | — | Y | N | N |
| Editor | Y | N | Y | Y | Y |
| Editor | Y | Y | Y | N | N |
| Editor | N | N | Y | Y | N |
| Editor | N | Y | Y | N | N |
| Admin | — | N | Y | Y | Y |
| Admin | — | Y | Y | Y | Y |
Row three and row five are the ones teams skip. An editor who owns a locked document is a real case, and the usual bug is that the interface hides the Edit button while the API still accepts PATCH /api/documents/482. Test permissions at both layers, always.
Row seven is worth a conversation. Should an admin be able to edit a locked document, or does the lock beat the role? The table forces someone to answer.
Example three: shipping logic
Conditions: destination is domestic, weight is 2 kg or less, express was selected.
Eight combinations again. The four that most test plans include: domestic light standard, domestic light express, international light standard, domestic heavy standard.
The four that get skipped:
- International, heavy, express. Many carriers do not offer this at all. What does the form show?
- International, light, express with a country that has no express service. Expect a clear message, not an empty dropdown.
- Domestic, heavy, express where the weight pushes the price into a new band. Check the band edge at exactly 2.00 kg.
- Any combination with a free-shipping promo. Free shipping plus paid express is a rule collision, and the total is often wrong.
When one of these fails, the useful bug report names the rule, not just the screen: "Rule 3 (international, over 2 kg, express) shows a shipping cost of 0.00 instead of blocking express." Attaching the request and console output at the same moment saves a round of questions, which is the part a browser-based reporter like Crosscheck handles for you while you are still on the page.
Turning the table into test cases
- Number every column. R1, R2, R3, and so on.
- Write the setup for each column as concrete data: a real account, a real cart, a real document.
- Write the expected result before running anything.
- Run the columns and record pass or fail against the rule number.
- Keep the table in the ticket. When the rules change, edit the table and the test cases follow.
Referring to rules by number is a small habit with a large payoff. "R6 fails" is precise, searchable, and understood by everyone who has the table.
Mistakes that waste the effort
Using it for the wrong problem. If each field is judged on its own, a decision table adds nothing. Use equivalence partitioning instead. Decision tables earn their place only when conditions interact.
Too many conditions in one table. Six conditions give 64 columns. Split the table by area, or switch to pairwise testing, which covers every pair of values in far fewer cases.
Conditions that are not yes or no. "Plan" with three values is not a condition, it is three. Either split it into separate yes/no conditions or use a multi-value row like the Role row above, and recount the combinations.
Building the table after the tests. The table exists to find the cases you would not have written. Build it first.
Frequently asked questions
How many conditions is too many for one decision table?
Four is comfortable at 16 columns. Five gives 32 and is still workable after collapsing. At six or more, split the table or use pairwise testing.
What is the difference between a decision table and a truth table?
A truth table shows logical results only. A decision table adds actions, so it says what the system should do, not just whether an expression is true.
Do I have to test every column?
Test every column that remains after collapsing and after removing impossible combinations. Those are usually far fewer than the original count, and each one covers a rule no other test covers.
Who should write the decision table?
Whoever understands the rules, ideally with a tester in the room. Many teams build it during refinement, before any code is written, because the contradictions it exposes are cheapest to fix then.
Can decision tables be automated?
Yes. Each column maps neatly onto a parameterised test, where the conditions are the inputs and the actions are the assertions. The table becomes the data file for the test.




