Decision Tables & State Transitions
Some rules aren't a range of numbers — they're a mix of yes/no conditions. Two tidy tools make sure you never miss a combination, or a bad sequence. Both are just careful list-making.
The idea, in one line
When several yes/no conditions combine, write out every combination and its result. That grid is a decision table, and it stops you testing the happy path and calling it done.
How many rows?
- With n yes/no conditions you get 2 to the power of n combinations.
- 2 conditions -> 4 rows. 3 conditions -> 8 rows.
- The table guarantees you considered every one, not just the obvious ones.
See it work
In automation the table isn't a spreadsheet — it's just data: a list of rows fed to one test. New rule? Add a row.
# Decision table as data: 2 conditions -> 4 rows
# (in_window, item_returned, expected_action)
rules = [
(True, True, "refund"),
(True, False, "reject"),
(False, True, "reject"),
(False, False, "reject"),
]
def decide(in_window, item_returned):
return "refund" if in_window and item_returned else "reject"
for in_window, returned, expected in rules:
assert decide(in_window, returned) == expected
print(f"{len(rules)} rules covered") # 4 rules coveredRead it top to bottom: you listed all four combinations, wrote the rule once, then checked the rule against every row. Nothing slips through.
Advanced — when order matters
Decision tables catch bad combinations. But some features have memory, where the same action means different things depending on what happened before. That's where state transition testing comes in.
- A login that locks after three failures — the fourth try behaves differently from the first.
- An order moving new -> paid -> shipped — each step only makes sense from the right starting point.
- Test the legal moves and the illegal ones: can you ship an order that was never paid?
Grounded in Guru99's Decision Table Testing guide (aligned with ISTQB)