Black Box · Specification-Based

State Transition Testing

Model a system as a finite set of states and the events that move it between them. Test every valid transition — and verify that invalid transitions are rejected.

Junior Senior ISTQB CTFL v4.0 — 4.2.4

1 The Hook

A NZ courier company builds a parcel-tracking system. A parcel moves through states: Accepted, In Transit, Out for Delivery, Delivered. The team tests the obvious forward path and it all works — scan it in, move it along, mark it delivered. Customers are happy.

Then odd things start happening. A driver accidentally scans a parcel as "Out for Delivery" a second time after it was already marked Delivered, and the system cheerfully sends the customer a fresh "your parcel is on its way" text for a parcel sitting on their porch. Worse, a returned parcel gets scanned back to "In Transit" from "Delivered", and the proof-of-delivery record is silently overwritten. Nobody tested those moves because nobody asked "what happens if this event arrives when the parcel is in that state?"

The forward happy path is the easy 20%. The bugs were all in the transitions that should have been impossible — the events arriving in the wrong state. A system that only guards the steps it expects, and quietly accepts the ones it doesn't, ends up corrupting its own data. The only way to find these is to model every state and ask, for each event, whether it is allowed.

2 The Rule

Model the system as a fixed set of states and the events that move it between them, then test every valid transition at least once — and just as deliberately, test that invalid transitions are rejected rather than silently accepted.

3 The Analogy

Analogy

The traffic lights at a NZ intersection.

A set of traffic lights has a small number of states — red, green, amber — and strict rules about which can follow which. Green goes to amber, amber goes to red, red goes to green. What must never happen is red jumping straight to green with no amber, or two directions both showing green at once. The whole safety of the intersection rests on the impossible transitions staying impossible.

State transition testing is checking the lights. You confirm every allowed change works — and then you spend real effort trying to force the forbidden ones, because a light that will skip amber when poked is the one that causes the crash. Testing only "green then amber then red" in order misses the dangerous case entirely.

What it is

Many systems can only be in one state at a time, and move between states in response to events. A bank account is either Open, Frozen, or Closed. A booking is Pending, Confirmed, Cancelled, or Completed. State transition testing ensures all these paths work correctly.

The model has four elements:

  • States — the distinct conditions the system can be in.
  • Events (triggers) — inputs or actions that cause a transition.
  • Transitions — the move from one state to another.
  • Actions — what the system does when a transition occurs.

Building the state model

Start with a state diagram (boxes = states, arrows = transitions labelled with event/action). Then convert it to a transition table for easier test case derivation.

Worked example

Online order lifecycle: an order starts as Pending, can be Confirmed, Dispatched, Delivered, or Cancelled.

Order state transition table
Current StateEventNext StateAction
PendingPayment receivedConfirmedSend confirmation email
PendingCustomer cancelsCancelledSend cancellation notice
ConfirmedStock allocated & shippedDispatchedSend tracking number
ConfirmedCustomer cancelsCancelledInitiate refund
DispatchedDelivery confirmedDeliveredClose order, request review
DeliveredCustomer cancelsInvalidError: cannot cancel delivered order
CancelledPayment receivedInvalidError: order is cancelled

Coverage criteria

There are three main coverage levels:

  • 0-switch (all states) — visit every state at least once. Weakest.
  • 1-switch (all transitions) — exercise every valid transition at least once. ISTQB Foundation standard.
  • 2-switch (all transition pairs) — every consecutive pair of transitions. Strong but expensive.

Testing invalid transitions

A complete test suite also tests transitions that should not be possible. Try to cancel a delivered order. Try to dispatch a cancelled order. These tests verify the system rejects invalid state changes gracefully — not silently, and not by corrupting data.

Real-world value: state transition bugs are nasty to find manually. A user who somehow gets an order into an impossible state can create support nightmares. Model it formally and test the invalid paths explicitly.

ISTQB mapping

ISTQB CTFL v4.0 reference
RefTopicLevel
4.2.4State Transition TestingCTFL Foundation
FL-4.2.4 K3Apply state transition testing to derive test casesFoundation LO
FL-4.2.4 K3Achieve specified coverage levelsFoundation LO

NZ example — RealMe identity verification

RealMe is New Zealand’s government identity verification service (used by IRD, NZTA, MSD). A RealMe account moves through states during verification.

RealMe account — state transition table
Current StateEventNext StateNotes
UnverifiedSubmit documentsPendingDocuments under review
PendingDocuments acceptedVerifiedIdentity confirmed
PendingDocuments rejectedUnverifiedResubmit required
VerifiedFraud flag raisedSuspendedSuspicious activity detected
SuspendedFraud clearedVerifiedAccount reinstated
VerifiedClosure requestedClosedAccount closure
UnverifiedJump to VerifiedInvalidNo documents submitted — must be impossible
SuspendedSelf-close accountInvalidAdmin-only action; user cannot self-close while suspended

All-transitions coverage requires a test case for each valid transition. The two invalid transitions must also be tested — verify the system rejects them gracefully, not silently.

Try it yourself

NZ library book loan system — state transition table

A library book can be in one of four states: Available, On Loan, Reserved, or Overdue. There are exactly 6 valid transitions. For each row, select the From state and To state, and type the triggering event.

# From state Event (what triggers the change?) To state
All 6 valid transitions:
#From stateEventTo state
1AvailableMember borrows bookOn Loan
2On LoanMember returns bookAvailable
3OverdueMember returns bookAvailable
4On LoanDue date passes / book not returnedOverdue
5AvailableMember reserves bookReserved
6ReservedMember borrows / reservation fulfilledOn Loan

4 Now You Try

Three graded exercises — spot, fix, then build. Write your answer, run it for AI feedback, then compare to the model answer.

🔍 Exercise 1 of 3 — Spot: find the invalid transitions

A RealMe-style account has four states: Unverified, Pending, Verified, Suspended. The valid transitions are: Unverified→Pending (submit docs), Pending→Verified (docs accepted), Pending→Unverified (docs rejected), Verified→Suspended (fraud flag), Suspended→Verified (cleared). Identify three transitions that should be invalid and say what the system must do when each is attempted.

Show model answer
Any three of these well-explained earn full marks:

1. Unverified --jump straight to Verified--> Verified. No documents were ever submitted. The system must reject it; identity cannot be confirmed without the Pending review step.
2. Verified --submit documents--> Pending. Re-submitting docs for an already-verified account should be a no-op or rejected, not a silent move back to Pending that drops verified status.
3. Suspended --self-clear fraud--> Verified. A user cannot lift their own suspension; only an admin event (fraud cleared) may do that.
4. Unverified --fraud flag--> Suspended. There is nothing verified to suspend.

In every case the rule is the same: the system must reject the transition gracefully — show an error or ignore it — and must NOT silently change state or corrupt the record. Silently accepting an impossible event is the bug.
🔧 Exercise 2 of 3 — Fix: repair a broken transition table

A tester drafted the transition table below for a NZ online vehicle-registration renewal (states: Draft, Submitted, Paid, Complete). It is broken: one row has an impossible transition marked valid, and one genuinely valid transition is missing. Rewrite the table so it lists only valid transitions and add the missing one.

Flawed table:
Draft — submit → Submitted (valid)
Submitted — pay → Paid (valid)
Complete — pay again → Paid (valid) ← suspicious
Paid — (nothing listed) ← something missing?

Rewrite as a correct transition table:

Show model answer
Correct valid-transition table:
1. Draft --submit--> Submitted
2. Submitted --pay--> Paid
3. Paid --confirmation issued--> Complete

The impossible transition I removed: "Complete --pay again--> Paid". Once a renewal is Complete it is a terminal state; paying again must be rejected, not allowed to move backwards to Paid. Marking it valid would let a finished renewal be reopened and re-charged.

The valid transition I added: "Paid --confirmation issued--> Complete". The original table had no way to reach the Complete state at all, so the renewal could never finish. Every non-terminal state needs at least one outgoing valid transition.
🏗️ Exercise 3 of 3 — Build: a state model for a KiwiSaver withdrawal

Design a complete state model for a KiwiSaver first-home withdrawal application. Define the states, list every valid transition (from state, event, to state), and name at least two invalid transitions the system must reject. Aim for all-transitions (1-switch) coverage.

Show model answer
A strong model (yours may differ in naming but should have the same shape):

States: Draft, Submitted, Under Review, Approved, Declined, Paid Out.

Valid transitions:
1. Draft --submit application--> Submitted
2. Submitted --provider begins review--> Under Review
3. Under Review --evidence accepted--> Approved
4. Under Review --evidence insufficient--> Declined
5. Approved --funds released--> Paid Out
6. Declined --applicant resubmits--> Submitted

Invalid transitions to reject:
1. Submitted --funds released--> Paid Out. Cannot pay out before review and approval; must be rejected.
2. Paid Out --resubmit--> Submitted. Paid Out is terminal; a completed withdrawal cannot be reopened.
3. (bonus) Declined --funds released--> Paid Out. A declined application must never pay out.

All-transitions coverage means one test per valid transition (six tests), plus explicit tests that each invalid transition is rejected gracefully without corrupting state.

Self-Check

Click each question to reveal the answer.

Q1: What are the four elements of a state transition model?

States (the distinct conditions the system can be in), events or triggers (inputs that cause a change), transitions (the move from one state to another), and actions (what the system does when a transition occurs).

Q2: Name the three main coverage levels and which one is the ISTQB Foundation standard.

0-switch (all states — visit every state once, weakest), 1-switch (all valid transitions — the ISTQB Foundation standard), and 2-switch (all transition pairs — strong but expensive). 1-switch / all-transitions is the expected baseline.

Q3: Why is testing invalid transitions just as important as testing valid ones?

Because the worst bugs are events arriving in a state where they should be impossible — a delivered parcel scanned back to in-transit, a completed payment re-charged. If the system silently accepts these, it corrupts its own data. You must verify it rejects them gracefully.

Q4: What does it mean for a state to be "terminal", and what must you check about it?

A terminal state (e.g. Completed, Paid Out, Closed) has no valid outgoing transitions — the workflow ends there. You must check that every event arriving in a terminal state is rejected, so a finished item cannot be reopened, re-charged, or moved backwards.

Q5: How do you turn a state diagram into test cases?

Convert the diagram (boxes = states, arrows = transitions) into a transition table listing current state, event, next state, and action. Each valid row becomes a test case for all-transitions coverage, and each impossible state/event pairing becomes an invalid-transition test.