skip to content

questions

4

What are states, events, guards and transitions in a state transition model?

level: juniorimportance: must knowfreq 66%

answer

  1. Behaviour that depends on history
  2. Four parts: node, trigger, condition, arrow
  3. Same event, different outcome on data
  4. One arrow yields one test case
  5. Assert the target state and the action

basics

~20 s

A state is a condition the system rests in between inputs. An event is an incoming trigger. A guard is a condition that must hold for that trigger to fire. A transition is the resulting move plus any action it performs.

solid answer

~50 s

A **state** is a stable condition the system sits in while it waits for input, and it is remembered between requests. An **event** is the trigger that arrives: a user action, a message, a timer. A **guard** is a boolean condition attached to an event so that the same event can lead to different places depending on data. A **transition** is the arrow: source state plus event plus guard, leading to a target state, usually with an **action** as a side effect. Test cases fall straight out of the arrows: for each transition, drive the system into the source state, fire the event with data that satisfies the guard, then assert both the resulting state and the action. The state you assert is the oracle here, so the model is only useful if the resulting state is observable.

code

pseudocode · 12 lines
pseudocode
STATE Draft
  ON reserve WHEN onHand >= requested -> Reserved  / placeHold()
  ON reserve WHEN onHand <  requested -> Draft     / rejectShortStock()
  ON amend                            -> Draft     / rewriteLines()
  ON cancel                           -> Cancelled / closeRow()
  ON expire                           -> Expired   / closeRow()

TEST one_transition:
  given  row IS Draft AND onHand = 148 AND requested = 23
  when   fire reserve
  then   row IS Reserved
  and    holdCount = 1

go deeper

for a junior

Be ready to name the four parts — state, event, guard, transition — and to show that one arrow becomes one test case: reach the source state, fire the event, assert the target state and the side effect.

for a middle

An interviewer expects you to draw a small lifecycle from a prose requirement, decide what belongs in a guard rather than in a new state, and explain how the same arrows read as a grid of states against events.

for a senior

Show judgement about observability: states you cannot see from outside make untestable models. Talk about how you reach deep source states, and about asserting actions rather than the state field alone.

for a principal

Own the question of where this technique earns its keep — lifecycle-shaped features, protocol handlers, devices — and where a stateless technique is cheaper. Decide who owns the model and how it stays in step with the implementation.

### Why model behaviour as states at all Some systems answer a request purely from its inputs. Many do not: the same request produces a different answer depending on what happened earlier. A booking that can be cancelled today cannot be cancelled once it has shipped; a session that accepts a password attempt stops accepting them after several failures. Whenever the correct response depends on history, the history has been compressed into something the system remembers, and that something is a **state**. Modelling it explicitly turns vague prose requirements into a diagram or a table you can count, argue about, and derive cases from. ### The four elements **State.** A named condition the system rests in between events. States are not variables; they are equivalence classes over the whole history. Good states are observable — you can tell from the outside which one you are in — and mutually exclusive, so at any instant exactly one is current. A state that no test can observe is a modelling problem, because you cannot assert on it. **Event.** The trigger that arrives while the system is in a state: an operator action, an inbound message, the expiry of a timer, a scheduled job. Events are the alphabet of the model. Listing them exhaustively is half the work, and the ones people forget are the non-human ones — timeouts, retries, compensating messages. **Guard.** A boolean condition attached to a transition. It lets the same event in the same state lead to two different outcomes depending on data. Guards are how you avoid inventing a new state for every data variation. If two guards on the same state-event pair can be true at once, the model is ambiguous; if neither can be true, that combination has no defined behaviour. **Transition.** The arrow itself: source state + event + guard -> target state, usually carrying an **action** — a side effect such as writing a ledger row, sending a notification, or releasing a lock. A transition whose target equals its source is a self-loop, and self-loops are still transitions that need a test. ### A worked model: a warehouse stock ledger A reservation row in a warehouse stock ledger has six states — Draft, Reserved, Picked, Shipped, Cancelled, Expired — and seven events: reserve, amend, cancel, expire, pick, ship, split. Ten arrows are specified: - Draft: reserve (guard: on-hand quantity at least requested) -> Reserved, action place hold; amend -> Draft; cancel -> Cancelled; expire -> Expired - Reserved: pick -> Picked; amend (guard: quantity unchanged) -> Reserved; cancel -> Cancelled, action release hold; expire -> Expired, action release hold - Picked: ship -> Shipped; cancel -> Cancelled, action return stock - Shipped, Cancelled and Expired are terminal: no arrows leave them. Each arrow is one test case. Drive the row into the source state by replaying the shortest path to it, fire the event, assert the target state **and** the action. Asserting only the state is the common shortcut and it misses the interesting defects: a cancel that lands in Cancelled but never releases the hold looks green on state alone. ### Reading the model as a table The same ten arrows fit a grid of six states by seven events — forty-two cells, ten of which carry a specified target. The tabular form makes the gaps visible in a way the diagram never does: the thirty-two cells nobody drew are exactly the combinations the specification never discussed, and they are where the interesting negative cases live. ### Where the technique fits State transition modelling is a behavioural design technique: it derives cases from *sequence*, not from the shape of any one input value. Choosing which quantity to submit is a different question answered by a different technique; this one decides which order to fire events in and what to assert afterwards. It pays for itself on workflow-shaped features — orders, claims, devices, protocol handlers, anything with a lifecycle — and it is close to worthless on a stateless calculation. ### Common mistakes Inventing states for data that a guard should carry, which multiplies the model. Modelling internal implementation flags rather than externally visible conditions, which produces a model no black-box test can assert. Leaving timers and inbound messages out of the event list, so entire branches of the real behaviour never appear. And treating the drawing as the deliverable: the deliverable is the set of test cases the drawing generates.

  • Why is a guard preferable to inventing an extra state?
    A guard keeps a data condition out of the state space. If you turn every data variation into its own state, the number of states multiplies with each variable and the model becomes unmaintainable, while the behaviour it describes is unchanged. Guards let one state-event pair fan out on data. The cost is that each guard splits one arrow into several cases, so you must test both the satisfied and the unsatisfied branch.
  • What do you assert at the end of a transition test besides the target state?
    The action. A transition usually has a side effect — a hold placed or released, a ledger row written, a notification sent — and defects hide there far more often than in the state field. Assert the target state, the action's observable result, and that nothing else moved. If the target state is not observable from outside, treat that as a testability defect and ask for an accessor rather than reaching into internals.
  • How do you get the system into the source state for a transition test?
    Replay the shortest event sequence that reaches it, or set it up directly through a documented back door if one exists. Replaying is more honest and catches setup defects, but makes every test depend on earlier transitions, so a broken early arrow fails a whole column of tests. Many teams replay for short paths and seed directly for deep states, and record which approach each case uses so failures are diagnosable.

A vending machine: the state is what it is waiting for, the event is the coin or the button, the guard is whether there is stock behind that button, and the transition is the clunk that leaves it waiting for something else.

saying these in an interview costs you the question

  • Calling any internal variable a state
  • Modelling states no black-box test can observe
  • Forgetting timers and inbound messages as events
  • Asserting the target state but never the action
  • Creating a new state for every data variation
  • Treating the diagram, not the test cases, as the deliverable

context

open as a page

In state transition testing, what is the difference between 0-switch and 1-switch coverage?

level: middleimportance: must knowfreq 57%

basics

~10 s

0-switch coverage exercises every single valid transition at least once. 1-switch coverage exercises every valid pair of consecutive transitions, so it catches defects that only appear when one move follows another.

open as a page

What do blank cells in a state transition table mean, and how do you test them?

level: middleimportance: should knowfreq 45%

basics

~20 s

A blank cell is a state-event pair the specification never defined. Test it by driving the system into that state, firing that event, and checking the system rejects or ignores it safely instead of quietly moving somewhere the model never drew.

open as a page

How do you contain state explosion in a behaviour model that is too large to test?

level: seniorimportance: nice to knowfreq 22%

basics

~10 s

Stop encoding data in states. Move data conditions into guards, fold related states under a superstate, split independent concerns into separate parallel models, and cover only the risky regions at a higher switch level.

open as a page