Decision Table Testing
Equivalence Partitioning and Boundary Value Analysis are great for testing one field at a time. But plenty of real behavior in RecipeBox depends on several conditions combining together — for example, the "Publish Recipe" button doesn't check just one thing before it lets a recipe go live; it checks whether the title is filled in, whether there's at least one ingredient, and whether there's at least one step, all at once. When a business rule depends on multiple independent yes/no conditions like this, testing them one at a time misses the combinations — and combinations are exactly where logic bugs like "it let me publish with no ingredients because I had a title and a step" tend to hide. Decision Table Testing is the technique built for exactly this situation.
What Is Decision Table Testing?
A decision table is a grid that lists every possible combination of a set of input conditions, and
for each combination, the action or outcome the system is supposed to produce. If you have
n independent yes/no (Boolean) conditions, there are 2^n possible
combinations — for 3 conditions, that's 2 × 2 × 2 = 8 combinations,
each one a separate test case. Building the table forces you to think through every combination
deliberately, rather than only testing the "everything is fine" case and the "everything is
missing" case and assuming the in-between combinations will just work.
Worked Example: RecipeBox's "Publish Recipe" Button
The requirement for publishing a recipe is: a recipe can only be published if it has a title, at least one ingredient, and at least one step. Any missing piece should block publishing and tell the user exactly what's missing. That gives us three Boolean conditions:
- C1 — Title filled in? (Y/N)
- C2 — At least 1 ingredient? (Y/N)
- C3 — At least 1 step? (Y/N)
With 3 conditions, the full decision table has 8 rules:
| Rule | C1: Title? | C2: Ingredient? | C3: Step? | Action / Expected Result |
|---|---|---|---|---|
| R1 | Y | Y | Y | Allow publish — recipe goes live |
| R2 | Y | Y | N | Block — error: "Add at least one step" |
| R3 | Y | N | Y | Block — error: "Add at least one ingredient" |
| R4 | Y | N | N | Block — errors: "Add at least one ingredient" and "Add at least one step" |
| R5 | N | Y | Y | Block — error: "Title is required" |
| R6 | N | Y | N | Block — errors: "Title is required" and "Add at least one step" |
| R7 | N | N | Y | Block — errors: "Title is required" and "Add at least one ingredient" |
| R8 | N | N | N | Block — all three errors shown |
Only R1 allows the publish action — every other rule must block it and show the right combination of error messages. If a tester only checked R1 (everything filled in) and R8 (everything empty), they'd completely miss a bug where, say, RecipeBox allows publishing whenever the title and at least one ingredient are present, regardless of whether there are any steps (a bug hiding specifically in R2). That's the kind of bug decision tables are designed to catch.
How to Build a Decision Table Step by Step
- List the conditions. Identify every independent input that affects the outcome — here, title / ingredient / step.
- Work out the number of combinations.
2^nfornBoolean conditions. For non-Boolean conditions (e.g. a condition with 3 possible states), multiply accordingly. - Enumerate every combination systematically — don't just write down the ones that seem "interesting." The value of the technique is precisely that it doesn't rely on your intuition about which combinations matter.
- Define the expected action for each row, ideally by getting the actual business rule confirmed by whoever owns the requirement, since this is exactly the kind of detail ("what error do we show when two things are missing at once?") that often turns out to be undocumented.
- Turn each row into a test case — set up the preconditions described by that row, perform the action, and check the actual result against the expected action.
When to Reach for Decision Tables
Common Mistakes With Decision Tables
- Skipping "impossible-seeming" combinations. A combination that seems like it "shouldn't happen" is often exactly the one nobody coded a path for.
- Not confirming the expected result for every row with product/dev. Guessing the expected outcome defeats the purpose — you end up testing your own assumption, not the actual requirement.
- Forgetting to check the error message contents, not just "did it block or not." R4, R6, and R7 above all block — but only correctly if they show the right combination of messages, not just any one of them.